SOLVED, C++ vector of vectors hell

Started by
25 comments, last by darenking 18 years, 9 months ago
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.
Advertisement
Yea I wasnt sure why exactly the op wanted a pointer.. Could just pass the vector as reference to a function without using its address etc.
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?
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")); }
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).
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;}
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.
my siteGenius is 1% inspiration and 99% perspiration
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...).
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.cppstd::vector<int> *pVec = &m_Cycles[data1];m_Characters[character]->AnimCyclePut(action, *pVec);


...and received and stored like this:

//in character.hprivate:	std::vector<int> m_AnimCycle;//in character.cppvoid 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?
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}

This topic is closed to new replies.

Advertisement