user1413793
user1413793

Reputation: 9347

C++ Array Size Initialization

I am trying to define a class. This is what I have:

enum Tile {
GRASS, DIRT, TREE 
};

class Board {
public:
    int toShow;
    int toStore;
    Tile* shown;
    Board (int tsh, int tst);
    ~Board();
};

Board::Board (int tsh, int tst) {
    toShow = tsh;
    toStore = tst;
    shown = new Tile[toStore][toStore]; //ERROR!
}

Board::~Board () {
    delete [] shown;
}

However, I get the following error on the indicated line -- Only the first dimension of an allocated array can have dynamic size.

What I want to be able to do is rather then hard code it, pass the parameter toShow to the constructor and create a two-dimensional array which only contains the elements that I want to be shown.

However, my understanding is that when the constructor is called, and shown is initialized, its size will be initialized to the current value of toStore. Then even if toStore changes, the memory has already been allocated to the array shown and therefore the size should not change. However, the compiler doesn't like this.

Is there a genuine misconception in how I'm understanding this? Does anyone have a fix which will do what I want it to without having to hard code in the size of the array?

Upvotes: 2

Views: 2659

Answers (5)

Israel Unterman
Israel Unterman

Reputation: 13520

Let me tell you about what I did when I needed a 3D array. It might be an overkeill, but it's rather cool and might help, although it's a whole different way of doing what you want.

I needed to represent a 3D box of cells. Only a part of the cells were marked and were of any interest. There were two options to do that. The first one, declare a static 3D array with the largest possible size, and use a portion of it if one or more of the dimensions of the box were smaller than the corresponding dimensions in the static array.

The second way was to allocate and deallocate the array dynamically. It's quite an effort with a 2D array, not to mention 3D.

The array solution defined a 3D array with the cells of interest having a special value. Most of the allocated memory was unnecessary.

I dumped both ways. Instead I turned to STL map. I define a struct called Cell with 3 member variables, x, y, z which represented coordinates. The constructor Cell(x, y, z) was used to create such a Cell easily. I defined the operator < upon it to make it orderable. Then I defined a map<Cell, Data>. Adding a marked cell with coordinates x, y, z to the map was done simply by

my_map[Cell(x, y, z)] = my_data;

This way I didn't need to maintain 3D array memory management, and also only the required cells were actually created.

Checking if a call at coordinate x0, y0, z0 exists (or marked) was done by:

map<Cell, Data>::iterator it = my_map.find(Cell(x0, y0, z0));
if (it != my_map.end()) { ...

And referencing the cell's data at coordinat x0, y0, z0 was done by: my_map[Cell(x0, y0, z0)]...

This methid might seem odd, but it is robust, self managed regarding to memory, and safe - no boundary overrun.

Upvotes: 1

Kamran Amini
Kamran Amini

Reputation: 1062

You can use a one dimensional array. You should know that bi-dimensional arrays are treated as single dimensional arrays and when you want a variable size you can use this pattern. for example :

int arr1[ 3 ][ 4 ] ;
int arr2[ 3 * 4 ] ;

They are the same and their members can be accessed via different notations :

int x = arr1[ 1 ][ 2 ] ;
int x = arr2[ 1 * 4 + 2 ] ;

Of course arr1 can be seen as a 3 rows x 4 cols matrix and 3 cols x 4 rows matrix. With this type of multi-dimensional arrays you can access them via a single pointer but you have to know about its internal structure. They are one dimensional arrays which they are treated as 2 or 3 dimensional.

Upvotes: 2

djechlin
djechlin

Reputation: 60848

It's good to understand a little of how memory allocation works in C and C++.

char x[10];

The compiler will allocate ten bytes and remember the starting address, perhaps it's at 0x12 (in real life probably a much larger number.)

x[3] = 'a';

Now the compiler looks up x[3] by taking the starting address of x, which is 0x12, and adding 3*sizeof(char), which brings to 0x15. So x[3] lives at 0x15.

This simple addition-arithmetic is how memory inside an array is accessed. For two dimensional arrays the math is only slightly trickier.

char xy[20][30];

Allocates 600 bytes starting at some place, maybe it's 0x2000. Now accessing

xy[4][3];

Requires some math... xy[0][0], xy[0][1], xy[0][2]... are going to occupy the first 30 bytes. Then xy[1][0], xy[1][1], ... are going to occupy bytes 31 to 60. It's multiplication: xy[a][b] will be located at the address of xy, plus a*20, plus b.

This is only possible if the compiler knows how long the first dimension is - you'll notice the compiler needed to know the number "20" to do this math.

Now function calls. The compiler little cares whether you call

foo(int* x);

or

foo(int[] x);

Because in either case it's an array of bytes, you pass the starting address, and the compiler can do the additional to find the place at which x[3] or whatever lives. But in the case of a two dimensional array, the compiler needs to know that magic number 20 in the above example. So

foo(int[][] xy) {
    xy[3][4] = 5;     //compiler has NO idea where this lives 
                      //because it doesn't know the row dimension of xy!
}

But if you specify

foo(int[][30] xy)

Compiler knows what to do. For reasons I can't remember it's often considered better practice to pass it as a double pointer, but this is what's going on at the technical level.

Upvotes: 0

Robᵩ
Robᵩ

Reputation: 168876

Use C++'s containers, that's what they're there for.

class Board {
public:
    int toShow;
    int toStore;
    std::vector<std::vector<Tile> > shown;
    Board (int tsh, int tst) : 
      toShow(tsh), toStore(tst), 
      shown(tst, std::vector<Tile>(tst))
    {
    };
};

...

    Board board(4, 5);
    board.shown[1][3] = DIRT;

Upvotes: 3

user529758
user529758

Reputation:

First, if you want to refer to a 2D array, you have to declare a pointer to a pointer:

Tile **shown;

Then, have a look at the error message. It's proper, comprehensible English. It says what the error is. Only the first dimension of an allocated array can have dynamic size. means -- guess what, that only the first dimension of an allocated array can have dynamic size. That's it. If you want your matrix to have multiple dynamic dimensions, use the C-style malloc() to maintain the pointers to pointers, or, which is even better for C++, use vector, made exactly for this purpose.

Upvotes: 0

Related Questions