2

I've looked around trying to figure out if I should return a 2D array in C++, but got mixed answers.

Some of the answers say no because the data is local to the function and when it returns, the array points to "junk data" (as it can be overwritten at any time). However, some said "yes, return it like a normal array."

So, to my dilemma:

I have a 2D array that holds pointers to Tile objects

Tile* map[128][128];

Should I return the array in a function? Why or why not? If the answer is yes, how would I do that?

EDIT: I was unclear. I want to make a getter method to return the map variable and be able to use the pointers in the array in another function.

8
  • If you're creating the objects in the function, then yes, you need to dynamically allocate them if you don't want dangling pointers. Commented Dec 11, 2012 at 1:59
  • @chris The array is part of a class. I clean it up properly. Commented Dec 11, 2012 at 2:00
  • You can't return arrays from a function anyway, it's not allowed by the language. Can you be a bit more specific what the situation is? Commented Dec 11, 2012 at 2:00
  • @CodingMash No, it's an array of arrays of pointers, commonly an array of arrays is called a 2D array. Commented Dec 11, 2012 at 2:02
  • @CodingMash ummm, not its not Commented Dec 11, 2012 at 2:02

3 Answers 3

4

You could do that... the problem is that your caller is now responsible for freeing the memory. And he might not know how you allocated it. Should he call free()? delete[]? A free routine provided by the OS? Or one provided by some other memory caching system in your app?

Two common ways around this are:

  • Have the caller allocate the memory and your function merely populates it
  • Use a C++ array class; here you might have a std::vector containing a std::vector of Tile*. This is great as it absolves you of manually dealing with the memory allocation/deallocation.

But then... who's going to free the Tile* instances? And with what API? So perhaps you need a vector of vectors of Tile, rather than Tile*.

vector<vector<Tile>> map;
Sign up to request clarification or add additional context in comments.

8 Comments

Wait, the caller is responsible for freeing the memory? The map variable is inside of a class, can the class not call internal methods to delete the pointers?
If that's the case, then when are you trying to return it from a function? Is this simply a getter method? Sorry, I thought this was a function to generate the map. In that case have your function return type be Tile**. e.g. Tile** getMap() { return map; }
smart_ptr or unique_ptr would also be very useful in this situation.
@GrahamPerks Yea, this is just a getter method. @slavik262 I don't know what smart_ptr or unique_ptr are; I'll research that some.
@GrahamPerks Compiling under GCC, it says Tile** is not the correct type for map. It says map is of the type Tile* (*)[128]. How would I return it then?
|
3

The 2 answers you are getting is due to the two different ways this is possible.

Stack based - local memory, deallocated and the pointer returned is invalid

typedef int *(*TileGrid)[128];
Tile* map[128][128];
....
TileGrid GetTiles(){
    return map;
}

Heap Allocated - allocated on heap and not deallocated after the function ends, up to caller of this function to deallocate - safe.

typedef int *(*TileGrid)[128];
TileGrid map = new Tile*[128][128];
....
TileGrid GetTiles(){
    return map;
}

Recommended - C++ way - array instead of vector since size is compile time constant (true C++ way would be to use std::unique_ptr<Tile> instead of Tile*, but 1 issue at a time. These containers automatically manage the memory for you, and std::array can be used as if it is a regular array.

typedef std::array<std::array<Tile*,128>,128> TileGrid;
TileGrid map ;
...
TileGrid& GetTiles(){
    return map;
}

Return by reference to prevent an expensive copy, and because the function is just a getter and the object maintains ownership of the map.

Edit: fixed for a getter function

5 Comments

You're talking about returning 64 kilobyte objects, which can't take advantage of move semantics, by value. You'll be saved in many cases by copy elision, but I think vector would be better here, since it can take advantage of move semantics when copy elision fails.
I havent read up fully on the new move semantics, alternatively we could also pass in TileGrid as a reference.
looks like OP is writing a getter, not a generator, In that case returning a reference would be my choice
Both your "two ways" are not valid and do not compile
wow syntax gets involved, not even sure how to return without a typedef.. fixed.. just use std::array!
1

C/C++ has two types of memory - stack and heap. Usually variables are allocated on the stack which is temporary storage and is marked for re-use whenever you exit the current scope - like when you end a loop or return from a function.

When you use new it will allocate heap memory which will stay around until you use delete but you have to be careful that you match up all the new and delete calls so that you don't get a memory leak. There are many strategies for managing this situation in C++. You can google for 'memory management in C++' to learn more.

In your simple case, I would recommend avoiding pointers but using an stl container to build your array of tiles. For example a std::vector like vector< vector<Tile> > map. You can use the same square brackets syntax to access the map e.g. map[x][y] = Tile(); That way the memory allocation and management of the array is handled in the vector class.

1 Comment

C++ does not say anything about a stack or a heap. It mentions storage specifiers but not how they are implemented.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.