Pointer to 2D array of objects (C++)

Programming, for all ages and all languages.
Post Reply
User avatar
AndrewAPrice
Member
Member
Posts: 2303
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Pointer to 2D array of objects (C++)

Post by AndrewAPrice »

I'm writing a game with tile based map. The map can contain a dynamic number of tiles, each Tile being its own object (it contains info about what sort of tile it is along with a 3D mesh).

In my Tile Manager class (a class which handles all the map tiles) I have made a pointer to a 2D array of Tile objects:

Code: Select all

Tile **m_tiles;
How can I allocate this pointer and the tile objects?

I have thought of doing something like:

Code: Select all

	Tile tiles[m_width][m_height];

	for(int x = 0; x < m_width; x++)
		for(int y = 0; y < m_height; y++)
		{
			tile = new Tile(x,y,TileType::Grass);
		}

	m_tiles = &Tile;
But, I get the following errors:

Code: Select all

1>.\tilemanager.cpp(21) : error C2057: expected constant expression
1>.\tilemanager.cpp(21) : error C2466: cannot allocate an array of constant size 0
1>.\tilemanager.cpp(21) : error C2057: expected constant expression
1>.\tilemanager.cpp(21) : error C2466: cannot allocate an array of constant size 0
1>.\tilemanager.cpp(21) : error C2087: 'tiles' : missing subscript
1>.\tilemanager.cpp(21) : error C2133: 'tiles' : unknown size
1>.\tilemanager.cpp(21) : error C2512: 'Tile' : no appropriate default constructor available
I'm going to use a linked list, but if any one knows how to create a pointer to a 2D array I would be greatfull.

A 2D array would be faster - I could look up a tile by using m_tile[x][y] rather than doing thousands of searches through a linked list (the map size could but up to 100x100 tiles).
My OS is Perception.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Post by Colonel Kernel »

C++ doesn't support 2D arrays directly in the language, unless the sizes of both dimensions are known at compile-time. For dynamically-sized 2D arrays you sort of have to "fake it" using an array of arrays, or a 1D array where you calculate the array index from your two "2D" indices. I think there might be a way to make this prettier via operator overloading, but it's been a while...

First, I will explain why the compiler is complaining (you learn a lot by understanding compiler diagnostics):

Code: Select all

   // You can't do this because m_width and m_height are variables.
   // The syntax you're using is telling the compiler to create a fixed-size
   // 2D array on the stack, but it can't be fixed-size because m_width
   // and m_height are variables.
   // Also, I hope you have a default constructor defined for Tile, because
   // this is declared as an array of Tile objects, not pointers to Tile
   // objects.
   Tile tiles[m_width][m_height]; 

   for(int x = 0; x < m_width; x++) 
      for(int y = 0; y < m_height; y++) 
      { 
         // I don't see the variable "tile" declared anywhere. I hope it's
         // a Tile*, because otherwise this won't work.
         tile = new Tile(x,y,TileType::Grass); 
      } 

   // You can't take the address of a type. If you meant &tiles, that's
   // a fairly dangerous thing to do because stack-allocated objects
   // disappear as soon as the function in which they are declared exits.
   m_tiles = &Tile; 
For the array of arrays approach, try this (warning: I haven't tried compiling it, so there may be a little error or two):

Code: Select all

    // Assuming that m_width and m_height are both of type int, and
    // that m_tiles is of type Tile***. There are three stars because
    // it is a 2D array of pointers to Tile objects.
    m_tiles = new Tile**[m_width]; // Create the array of arrays.
    for(int x = 0; x < m_width; x++)
    {
        m_tiles[x] = new Tile*[m_height]; // Create each "column"

        for(int y = 0; y < m_height; y++) 
        { 
            // This is why m_tiles must be Tile*** -- because operator new
            // returns a Tile*, not a Tile.
            m_tiles[x][y] = new Tile( x, y, TileType::Grass ); 
        } 
    }
Don't forget to delete everything when you're done with it. If you're doing this all manually just to learn the language, that's fine, but in reality a better way to do this would be to use STL vectors (this code is also untested):

Code: Select all

    // Assuming that m_width and m_height are both of type int, and
    // that m_tiles is of type std::vector< std::vector<Tile*> >.

    // You can also construct it to be the right size if you use an
    // initialization list in your constructor.
    m_tiles.resize( m_width ); // This creates each "column" for you.
    for(int x = 0; x < m_width; x++)
    {
        for(int y = 0; y < m_height; y++) 
        { 
            m_tiles[x].push_back( new Tile( x, y, TileType::Grass ) );
        } 
    }
You still need to delete each Tile in the vector-of-vectors when you're done using it, however. But it's a lot less to clean up than when using plain arrays.
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

In my Tile Manager class (a class which handles all the map tiles) I have made a pointer to a 2D array of Tile objects:
What in the world is going on here... I am not going to build a spaceship just to go a couple of meters in distance.
Tile **tiles;
Why do you need a pointer (or an array of pointers) to a pointer (or an array of pointers)?
Tile <pointer><pointer>tiles;

Watch:
mov eax, offset tiles
mov eax, dword [eax] // operation for each dereference of a pointer
mov eax, dword [eax] // ... second dereference.


All you need is:
Tile *m_tiles;
m_tiles = new Tile[m_width * m_height];
m_tiles[myX + myY * m_width]


You should actually save cycles since you need no memory fetch for calculating the index as you would when using a pointer to an array of pointers. <-- No expert here..
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Post by Colonel Kernel »

Kevin McGuire wrote:
In my Tile Manager class (a class which handles all the map tiles) I have made a pointer to a 2D array of Tile objects:
What in the world is going on here... I am not going to build a spaceship just to go a couple of meters in distance.
Tile **tiles;
Why do you need a pointer (or an array of pointers) to a pointer (or an array of pointers)?
Tile <pointer><pointer>tiles;

Watch:
mov eax, offset tiles
mov eax, dword [eax] // operation for each dereference of a pointer
mov eax, dword [eax] // ... second dereference.


All you need is:
Tile *m_tiles;
m_tiles = new Tile[m_width * m_height];
m_tiles[myX + myY * m_width]


You should actually save cycles since you need no memory fetch for calculating the index as you would when using a pointer to an array of pointers. <-- No expert here..
I said exactly this in far fewer words already:
For dynamically-sized 2D arrays you sort of have to "fake it" using an array of arrays, or a 1D array where you calculate the array index from your two "2D" indices.
But yes, that's probably the most efficient way to deal with it.
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

Talking about another thread where you did the exact same thing to me.
I know you did. :D But look at this post by hckr83, then scroll up to yours and then to mine. You will notice I already stated the answer in that thread, which you then posted afterwards (after mine).
http://www.osdev.org/phpBB2/viewtopic.p ... ght=#98993

Talking about this thread.
If I had noticed that I would have placed your words in quotes above mine to signify that you had indeed suggested a more optimal solution, but I would not have removed my post because it clearly and cleanly states and shows the optimal solution to the problem - very much like the thread above where I posted a bunch of garbage code then you came afterwards and posted a clean, elegant, and simple worded solution.

Sorry. :P
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Post by Colonel Kernel »

Kevin McGuire wrote:Sorry. :P
No worries. I assumed that the OP was actually interested in learning about multi-dimensional arrays in C++, not necessarily in finding the most optimal solution. I'm probably wrong on that point. :)

Something to watch out for though -- if you find yourself explaining things in code instead of English, it's usually a warning sign that you may not understand it enough yourself (or have not internalized it and abstracted it enough in your own mind).
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

I agree with you completely, and I feel I really need to explain more in plain English rather than giving examples in code since this is a sign that I do not completely understand what I am trying to do.

I will spend more time learning about what I am doing in a attempt to improve myself ever further. I appreciate the suggestion and constructive criticism. I really do appreciate your time and patience with me. It is something that was in no way required.

I will become even better as you have given me the desire to exceed my own intellectual limits.
Post Reply