# C++ Vector of Vectors problem

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

## Recommended Posts

Having a bit of trouble using a vector of vectors:
vector< vector<Tile> > tiles;
vector< vector<Tile> >::iterator iter_ii;
vector<Tile>::iterator iter_jj;

//calls a draw method on each Tile
void display()
{
for(iter_ii=tiles.begin(); iter_ii!=tiles.end(); iter_ii++)
{
for(iter_jj=(*iter_ii).begin(); iter_jj!=(*iter_ii).end(); iter_jj++)
{
(*iter_jj).draw();
}
}
}

{

for (int i = 0; i < rows; i++)
{
vector<Tile> t (columns);
tiles.push_back(t);
}
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
input >> ws;
char value;
input >> value;
if (value == 'k')
{
tiles[j] = Key(value, 32, 32, xpos, ypos, textures, p);
}
else
{
tiles[j] = Terrain(value, 32, 32, xpos, ypos, textures[0]);
}
xpos += 32;
}
xpos = 0;
ypos += 32;
}
input.close();
}


My problem is that when it calls the draw method, it seems to be calling it on the superclass Tile, which has an empty virtual function, rather than the overrided functions in the Terrain and Key classes. I can tell this because any print statement I put in the draw methods of Terrain or Key are not called. Any ideas what I am doing wrong?

##### Share on other sites
It looks like you've run into the 'slicing' problem.

Polymorphism in C++ doesn't work the way you seem to be expecting it to. To get the behavior you want, your 2D array needs to store pointers (of some sort) to Tile objects, not the tile objects themselves.

I'll go ahead and post this (as I'm guessing others have already posted by now), but I'll try to provide some more details here in a minute.

##### Share on other sites
Here's your code, revised to address the problem you mentioned (not compiled or tested):

//typedef boost::scoped_ptr<Tile> TilePtr;// Oops, that should be:typedef boost::shared_ptr<Tile> TilePtr;vector< vector<TilePtr> > tiles;vector< vector<TilePtr> >::iterator iter_ii;vector<TilePtr>::iterator iter_jj;//calls a draw method on each Tilevoid display(){    for(iter_ii=tiles.begin(); iter_ii!=tiles.end(); iter_ii++)    {        for(iter_jj=(*iter_ii).begin(); iter_jj!=(*iter_ii).end(); iter_jj++)        {            (*iter_jj)->draw();            }    }}void readFile() {//file reading stuff here    for (int i = 0; i < rows; i++)    {        vector<TilePtr> t (columns);        tiles.push_back(t);    }    for (int i = 0; i < rows; i++)    {        for (int j = 0; j < columns; j++)        {            input >> ws;            char value;            input >> value;            if (value == 'k')            {                tiles[j].reset(new Key(value, 32, 32, xpos, ypos, textures, p));            }            else            {                tiles[j].reset(new Terrain(value, 32, 32, xpos, ypos, textures[0]));            }            xpos += 32;        }                xpos = 0;        ypos += 32;    }    input.close();}

The differences between objects and pointers/references to those objects is a little much to take on in a single post, so I'll just recommend revisiting whatever C++ references you have available (or a good online reference such as the C++ FAQ Lite), and reviewing these topics, perhaps along with inheritance and polymorphism.

(I'm sure other will offer additional useful information here in this thread, but perhaps the above will at least get you pointed in the right direction in the meantime.)

[Edit: Fixed error in code.]

[Edited by - jyk on April 5, 2007 9:34:21 PM]

##### Share on other sites
Thanks for your quick reply. One question, do I have to include the boost library? Because I'm getting errors such as:

.\Main.cpp(35) : error C2653: 'boost' : is not a class or namespace name
.\Main.cpp(35) : error C2143: syntax error : missing ';' before '<'
.\Main.cpp(35) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
.\Main.cpp(36) : error C2065: 'TilePtr' : undeclared identifier

##### Share on other sites
Quote:
 Original post by PhoresisThanks for your quick reply. One question, do I have to include the boost library? Because I'm getting errors such as:.\Main.cpp(35) : error C2653: 'boost' : is not a class or namespace name.\Main.cpp(35) : error C2143: syntax error : missing ';' before '<'.\Main.cpp(35) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int.\Main.cpp(36) : error C2065: 'TilePtr' : undeclared identifier
Right, you'll need to grab the Boost libraries (much of Boost is on its way to standardization, but we're not quite there yet).

Building Boost can be a bit of a pain, but fortunately most of the libraries are header-only and don't require building. This includes all the smart pointer classes - just download the Boost libraries, add the proper include path, and you're off.

[Edit: Removed some incorrect information...]

[Edited by - jyk on April 5, 2007 9:07:05 PM]

##### Share on other sites
Quote:
 Original post by jyk[Edit: You could use std::auto_ptr instead, but scoped_ptr is a little more appropriate for the intended usage. Plus, you'll want to install the Boost libraries sooner or later, so now is probably as good a time as any :) ]

I don't believe either std::auto_ptr or boost::scoped_ptr are appropriate for using within a container. boost::shared_ptr works great, though.

##### Share on other sites
Yes, std::auto_ptr is definitely not appropriate for use in a container. auto_ptr has "move" semantics; that is, assigning one auto_ptr to another causes the assigned auto_ptr to contain NULL. boost::scoped_ptr is also not usable in containers.

##### Share on other sites
Indeed. To make this work, you'll want either a boost::shared_ptr, or a pointer-wrapper with copy semantics and objects implementing the "virtual clone idiom".

BTW, iterators have an operator-> that works just as you'd expect it to, so you don't need to do (*foo).bar nonsense. Also, we have more powerful tools at our disposal for such loops. Also, a rectangular map is more neatly expressed using (if we're reaching for Boost anyway) a boost::multi_array than a vector of vectors. Also, there's normally no need to .close() streams explicitly. Also, why would you use a global here for the tile map? Also, there are neater ways to handle 'xpos' and 'ypos' like that, by using the comma operator in the for loop conditions (and thus scoping those variables properly).

Not tested, but this should be more or less it:

typedef boost::shared_ptr<Tile> TilePtr;typedef boost::multi_array<TilePtr, 2> Tilemap;void drawTile(const TilePtr& ptr) {  ptr->draw();}void display(const Tilemap& map) {  std::for_each(map.begin(), map.end(), drawTile);}Tilemap readFile(const std::string& name) {  ifstream input(name.c_str());  int rows, columns;  input >> rows >> columns;  Tilemap result(boost::extents[rows][columns]);    for (int i = 0, ypos = 0; i < rows; i++, ypos += 32) {    for (int j = 0, xpos = 0; j < columns; j++, xpos += 32) {      char value;      input >> ws >> value;      if (value == 'k') {        result[j] = TilePtr(new Key(value, 32, 32, xpos, ypos, textures, p));      } else {        result[j] = TilePtr(new Terrain(value, 32, 32, xpos, ypos, textures[0]));      }    }          }  return result;}

##### Share on other sites
Quote:
 Original post by AgonyI don't believe either std::auto_ptr or boost::scoped_ptr are appropriate for using within a container. boost::shared_ptr works great, though.
Right, that should have been shared_ptr in my example, not scoped_ptr. Sorry about that.

1. 1
2. 2
Rutin
22
3. 3
4. 4
frob
18
5. 5

• 33
• 13
• 10
• 9
• 11
• ### Forum Statistics

• Total Topics
632567
• Total Posts
3007109

×