Sign in to follow this  

SOLVED, C++ vector of vectors hell

This topic is 4527 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've done something quite clever with vectors (created a vector of vectors) but I'm a bit out of my depth, and don't know how to pass one of the inner vectors to another object. In fact, I'm not 100% sure I've created my vectors correctly. I have a sequence of values called a Cycle, which is used for animation. These values are read in from a text file. The text file currently contains this: 2 3 4 5 4 3 2 6 7 8 7 6 -1 2 11 12 13 12 11 -1 5 6 5 6 -1 This is a list of three cycles, terminated with -1. They are read in and stored in the World object like this, in a loop which knows that there are three cycles:
//in World.h
	std::vector<int> m_Values;
	std::vector< std::vector<int> > m_Cycles;

//in World.cpp
	int data1;
	file >> data1;
	while( data1 != -1 )
	{
		m_Values.push_back(data1);
		file >> data1;
	}
	m_Cycles.push_back(m_Values);
	m_Values.clear();



Before I ask for help with passing one of the inner vectors, perhaps I should check I've got things right so far. Do I actually have three inner vectors (m_Values vectors) contained in one outer vector called m_Cycles? Or perhaps I have just deleted the data in the inner vector each time, and in fact have just one inner vector? Maybe I have to declare more than one m_Values vector in .h? Or maybe just declare the m_Cycles vector in .h and declare m_Values in the loop in .cpp? [Edited by - darenking on August 2, 2005 4:40:33 AM]

Share this post


Link to post
Share on other sites
Unless you loop the code you show from World.cpp three times, m_Cycles will only have one vector. Edit: Otherwise I think it works as you want it to.

Edit: Edit: When pushing m_Values into m_Cycles, what is added to m_Cycles is a copy of m_Values, so you won't need more m_Values vectors.

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
Do I actually have three inner vectors (m_Values vectors) contained in one outer vector called m_Cycles? Or perhaps I have just deleted the data in the inner vector each time, and in fact have just one inner vector?

When you push an object into a vector its value is copied, which means the answer to your first question is that every time you push m_Values into m_Cycles m_Cycles stores a copy of the m_Values you passed. So you aren't removing any data from inside m_Cycles when you change the values in m_Values after its been copyed to m_Cycles.
Quote:
Original post by darenking
Maybe I have to declare more than one m_Values vector in .h? Or maybe just declare the m_Cycles vector in .h and declare m_Values in the loop in .cpp?

declaring m_Cycles in the header file is enough. Then to access one of the pushed vectors you either use m_Cycles[1 (or whatever)] or iterate through them. If you want to get on of the values you can just do m_Cycles[1][1]. Essentially you've made a 2d array out of std::vectors.

Hope that helps.

Share this post


Link to post
Share on other sites
OK, I've got that working. Thanks for that.

Now my next problem is that I need to be able to pass a pointer or reference to one of these three inner vectors to another object, my Character object (sprites, essentially). The reason I want to pass by pointer or reference rather than by copy is that I may have hundreds of Characters and don't want to waste lots of memory.

How do I access one of the inner vectors so that I am able to pass it?

And how do I store it the other end?

Share this post


Link to post
Share on other sites
OK, how do I pass this pointer to another object?

Can I just pass it as basically a memory location, as though it were a pointer to an int rather than a pointer to a vector of ints? That way, I would surely be able to access the integers with squared brackets [] as when you access an array.

If so, what's the syntax? That's the really tricky bit for me, even if I get the concept I can never do the syntax.

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
OK, how do I pass this pointer to another object?

Can I just pass it as basically a memory location, as though it were a pointer to an int rather than a pointer to a vector of ints? That way, I would surely be able to access the integers with squared brackets [] as when you access an array.


You can pass it as a vector (as it is) and still be able to access it with square brackets. Just define your function or method to take a vector of that type (the one you want to pass).

If it's the inner one, then you can just index the outer one when you're passing it.

Share this post


Link to post
Share on other sites
Note the return type of the subscript operator on the first dimension is of type (const) std::vector<int>&.

i haven't read the entire thread but use a reference or constant reference instead otherwise you'll get confused on the syntax with pointers i have seen it happen many times [grin], just remember with either pointer/reference it has the same issues as pointer/reference to any other type.

Share this post


Link to post
Share on other sites
Quote:
Original post by Lotus
Quote:
Original post by darenking
OK, how do I pass this pointer to another object?


You can pass it as a vector (as it is) and still be able to access it with square brackets.


See what i mean [grin], if you have pointer to std::vector to use its subscript operator you first need to dereference the pointer i.e. (*vec_ptr)[index] not vec_ptr[index] which is also compilable but wrong logic/behaviour etc.

Share this post


Link to post
Share on other sites
example -



#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;


void printVector(vector<int>&rvec){
copy(rvec.begin(),rvec.end(),ostream_iterator<int>(cout,"\n"));
}

int main(){

vector< vector<int> > vec;
vector<int>veci;

for(int x =0; x < 10; x++){
veci.push_back(x);
}
vec.push_back(veci);
veci.clear();

for(int x =10; x < 20; x++){
veci.push_back(x);
}

vec.push_back(veci);
veci.clear();

for(int x =20; x < 30; x++){
veci.push_back(x);
}

vec.push_back(veci);
veci.clear();

for(int x = 30; x < 40; x++){
veci.push_back(x);
}

vec.push_back(veci);
veci.clear();


vector<int> *pVec = &vec[1];
printVector(*pVec);

pVec = &vec[2];
printVector(*pVec);

}

Share this post


Link to post
Share on other sites
Quote:
Original post by Gink

//...
vector<int> *pVec = &vec[1];
printVector(*pVec);

pVec = &vec[2];
printVector(*pVec);

//....


This is what i was saying earlier, the type of vec[1] & vec[3] is of type (const) std::vector<int>&, its already a reference type so using a pointer isn't required/mandatory, just make sure the variable is also reference type otherwise that is when a copy is made.

Share this post


Link to post
Share on other sites
Quote:
Original post by Gink
example -


void printVector(vector<int>&rvec){
copy(rvec.begin(),rvec.end(),ostream_iterator<int>(cout,"\n"));
}


I've got all this working. Thanks for that long example, that was a lifesaver.

Is that passing by reference or by pointer?

Final thing is, I want the receiving object to store the reference or pointer or whatever it is in a member variable so that it can be accessed by other methods. How do I store it?

Share this post


Link to post
Share on other sites
Pass by reference. The argument it receives is an alias.
Pass by pointer would mean the pointer isnt dereferenced and the method receives it with the & operator.

like


main(){
int *pt = new int(50);
method(pt);
delete pt;
}
void method(int &pt){ //pass by pointer


}





Anyway, heres an example of storing it in an object.



#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;


void printVector(vector<int>&rvec){
copy(rvec.begin(),rvec.end(),ostream_iterator<int>(cout,"\n"));
}

class StoreInfo{
public:
void setInfo(vector<int>&rvec){
vec = rvec;
}
vector<int>& getInfo(){
return vec;
}
private:
vector<int> vec;
};

int main(){

vector< vector<int> > vec;
vector<int>veci;

for(int x =0; x < 10; x++){
veci.push_back(x);
}
vec.push_back(veci);
veci.clear();

for(int x =10; x < 20; x++){
veci.push_back(x);
}

vec.push_back(veci);
veci.clear();

for(int x =20; x < 30; x++){
veci.push_back(x);
}

vec.push_back(veci);
veci.clear();

for(int x = 30; x < 40; x++){
veci.push_back(x);
}

vec.push_back(veci);
veci.clear();


vector<int> *pVec = &vec[1];
printVector(*pVec);

pVec = &vec[2];
printVector(*pVec);

printVector(vec[0]);

StoreInfo test;
test.setInfo(vec[3]);

vector<int> testVec = test.getInfo();

copy(testVec.begin(),testVec.end(),ostream_iterator<int>(cout,"\n"));

}

Share this post


Link to post
Share on other sites
Quote:
Final thing is, I want the receiving object to store the reference or pointer or whatever it is in a member variable so that it can be accessed by other methods. How do I store it?


By assigning the received pointer or reference to a member variable of the appropriate type; i.e. std::vector<int>& or std::vector<int>*. Nothing particularly fancy here.

However, you might want to consider the simpler alternative of just storing an array index in each Character object, and making the Character class aware of the m_Values.


class Character {
static vector<vector<int> > values; // moved from World - but you don't have to
int myValuesIndex;
int myFrameIndex;
public:
// ctor, other stuff... and:
int nextFrame() {
int result = values[myValuesIndex][myFrameIndex++];
myFrameIndex %= values[myValuesIndex].size();
return result;
}
}


Less efficient, but probably more robust (I do believe that any resizing of the big vector later, like because of adding new cycles determined at run-time, would invalidate any stored references, which would be pointing at the old memory allocation).

Share this post


Link to post
Share on other sites
Quote:

main(){
int *pt = new int(50);
method(pt);
delete pt;
}
void method(int &pt){ //pass by pointer


}


It would be a challenge to be more wrong than you. That does not compile, and is not an example of 'pass by pointer' (Your terminology is terrible as well). It is almost but not quite an example of passing a reference. What you meant was this:


void foo(int* iamanint) {/*...*/}

int main()
{
int an_int;
foo(&an_int);
return 0;
}

Share this post


Link to post
Share on other sites
You could just not use a vector of vectors. I'll show you how in a minute. Now, my solution may not be optimal for some purposes (like some types of databases), but for, oh, say, making a tilemap (which needs to have flat edges, something that isn't guaranteed by a vector of vectors), my solution has given me storage for the heightmap mesh in my new, awesome, REALTIME HEIGHTMAP EDITOR. Anyways, here it goes:

say you need a rectangular field of (width * height).

Well, here is what I would do (demonstration uses pointers, but you could just as easily, maybe more easily, use vectors):

Foo * foo_map;

//When resizing
delete [] foo_map;
foo_map = new Foo[width * height];

//When accessing a specific member by x and y coordinates
return (y * width + x];


Believe me, I have gone through the same hell you are experiencing right now, and I have since started using this method for all my rectangular field needs. Hope it helps.

Share this post


Link to post
Share on other sites
Quote:
Original post by silverphyre673
You could just not use a vector of vectors. I'll show you how in a minute.

...

foo_map = new Foo[width * height];

//When accessing a specific member by x and y coordinates
return (y * width + x];


No; in the OP's case, he wants to make use of the non-rectangularity offered by a vector of vectors (which is a *serious pain* to try to manage with a single array - at the very least you need a second array to hold the "offsets", and I think you can imagine the rest...).

Share this post


Link to post
Share on other sites
OK, here's where I've got to. I'm creating my 2D vector in World, with data read in from my dat.txt file, and sending one of the inner vectors to my Character object. It is sent like this:


//in world.cpp
std::vector<int> *pVec = &m_Cycles[data1];
m_Characters[character]->AnimCyclePut(action, *pVec);




...and received and stored like this:


//in character.h
private:
std::vector<int> m_AnimCycle;

//in character.cpp
void Character::AnimCyclePut(int action, std::vector<int>&vector)
{
m_AnimCycle = vector;
}




OK, so I'm storing my received vector in a vector called m_AnimCycle.

But is m_AnimCycle a pointer to one of the vectors in World, or a new vector? It's actually better if it's a new vector because I've now realised that what I need to do is create a new vector in Character that can be made up of any of the vectors from the 2D vector in world.

So, if the data in my text file is like this (each row terminated with -1)...

1 2 3 4 -1
77 88 99 -1
555 666 555 666 -1

...I will store each of these rows as three vectors in my 2D vector in World. I then may wish to send for example the 3rd and then the 1st of these vectors to Character where they are to be joined together in the same vector. So the vector in Character called m_AnimCycle should then contain this:

555 666 555 666 1 2 3 4

One vector, constructed from whichever vectors are sent to it. (May sound like an odd thing to need to do but it's how my animation works.)

If my understanding is correct and m_AnimCycle is a new vector of ints that exists only as a data member of Character, then this will be easy. I can just do a for/next loop to copy the contents of the received vector onto the end of m_AnimCycle with push_back. (Or is there an easier way? Can you just add a vector onto the end of a vector?)

Or am I totally wrong and m_AnimCycle is not a new vector of ints?

Share this post


Link to post
Share on other sites
It is a new vector.

In C++, things are of the type that you say you want them to be (assuming it compiles). You declare a vector of ints (not a pointer thereto) in your class, so that's what you get. The act of assignment simply has to compensate for that; in this case, that means making a copy of the vector data.

Of course, what you actually receive is a reference, which isn't quite the same thing as a pointer. Often, it's easier to think of the little '&' as specifying a calling convention, rather than being part of the type information. Anyway, there is no copy here just to pull the data into the function, but there is a copy for the assignment, so you're all good. :)

Edit:
Quote:

If my understanding is correct and m_AnimCycle is a new vector of ints that exists only as a data member of Character, then this will be easy. I can just do a for/next loop to copy the contents of the received vector onto the end of m_AnimCycle with push_back. (Or is there an easier way? Can you just add a vector onto the end of a vector?)


There is a good way that is "easy" at least in terms of writing the code; as a free bonus, it's an "algorithm" provided by the standard library, that will automatically be optimized (through template magic) for whatever data types you use it with (i.e. whatever type of container, and type of contained item). We need two pieces of the standard library:

1) The algorithm "std::copy", which copies stuff within a range (specified as beginning and ending "input iterators") to locations specified by an "output" iterator.

2) The helper function "std::back_inserter", which creates a special kind of output iterator called a "back_insert_iterator" for the type of container that it's called upon (again, this is templated). What this iterator does is continually specify the location "end of the container", which is of course continually updated as things are inserted at the end. Since the containers all automatically resize themselves to handle extra data, we get the effect of appending stuff to the end.

Thus, some demo code (tested):


#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator> // actually, algorithm will include iterator, because
// it's needed to make sense of back_inserter (and the back_insert_iterator
// class that it instantiates), but I prefer to explicitly include headers
// that are involved in things I explicitly use - and I have a vector<int>::
// iterator, so there you go.
using namespace std;

int main(int argc,char *argv[]){
vector<int> first, second;
for (int i = 0; i < 10; ++i) { first.push_back(i); }
for (int i = 0; i < 10; ++i) { second.push_back(9-i); }
// The magic part. BTW, the other for loops here can probably be replaced
// using other stuff in <algorithm> (and also <functional> and <numeric>)
// too, but that's beside the point ;)
std::copy(second.begin(), second.end(), back_inserter(first));
for (vector<int>::iterator x = first.begin(); x != first.end(); ++x) {
cout << *x << " ";
}
cout << endl;
// 0 1 2 3 4 5 6 7 8 9 9 8 7 6 5 4 3 2 1 0
}

Share this post


Link to post
Share on other sites
That's all working swimmingly.

The final, final thing, and probably a very simple thing, is that I need to pass this second vector, the one that exists as a data member of Character, to a third object, Actor (so that the actors take on the characteristics of the character).

So, forgetting about the previous stuff in World with the 2D vector... the Character object has this data member:

std::vector<int> m_AnimCycle;

All I want to do is send it in some way, by pointer or reference, to Actor. Bear in mind:

1 The values in the vector won't need to change, they're just set up at the start of the program, so it doesn't have to be received as a vector. It would nice and neat if it could be received as though it were an array, and could be just accessed with squared brackets.

2 The Actor object will already know how many values it contains so no probs with boundaries.

I want to do it the simplest way with the simplest syntax, unless of course there's a very good reason why not. Can I just in some way send the memory address of the start of the vector and receive it as though it were the start of an array?

Share this post


Link to post
Share on other sites
Quote:
Original post by darenkingI want to do it the simplest way with the simplest syntax, unless of course there's a very good reason why not. Can I just in some way send the memory address of the start of the vector and receive it as though it were the start of an array?


const int* Character::getAnimCycle(void) const { return &m_AnimCycle[0];}


simple and conforming.


Share this post


Link to post
Share on other sites
That compiles big time. But how do I store it and access the values in the receiving object, using squared brackets? What's the best way to store it as a data member?

Let's say I want to access the 3rd int in the array/vector...

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
That compiles big time. But how do I store it and access the values in the receiving object, using squared brackets? What's the best way to store it as a data member?

Let's say I want to access the 3rd int in the array/vector...



const int *p = myCharacter.getAnimCycle();
if( p[2] == 42)//meaning of life the universe and everthign found!
hooray();

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking

So, forgetting about the previous stuff in World with the 2D vector... the Character object has this data member:

std::vector<int> m_AnimCycle;

All I want to do is send it in some way, by pointer or reference, to Actor. Bear in mind:

1 The values in the vector won't need to change, they're just set up at the start of the program, so it doesn't have to be received as a vector. It would nice and neat if it could be received as though it were an array, and could be just accessed with squared brackets.


Pass the vector in by const reference. The vector *can* be treated "as though it were an array"; it overloads operator[]. The const correctness will protect you from changes, and passing a reference avoids copying the data.

You want to arrange for the Character to call an Actor method in this case; having the Actor just plain access Character data is probably "backwards" - objects generally are supposed to be responsible for their contents, and not simply allow others to grab them for use, but instead do the useful thing with their own contents when possible - and *send* their stuff outwards otherwise (delegation). There's a notable exception - containers - but the standard library provides all the containers you're ever likely to need.

However, I'd have to see more of the two classes and get a better sense of what they "do" to tell you more.

Share this post


Link to post
Share on other sites

This topic is 4527 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this