2

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?

user1413793
  • 9,057
  • 7
  • 30
  • 42
  • 2
    First of all, you'll want a 2D pointer: `Tile **shown;`. Read up on that a bit. Once you've learned how to do that, use `std::vector` instead. – chris Jun 04 '12 at 18:54
  • @R.MartinhoFernandes, I had to laugh at the effort that went into that for the reason you described. I did make my own `xarray` with C-style varargs once upon a time. – chris Jun 04 '12 at 18:57
  • 1
    Apart what was said by folks - you have a missed semicolon just before the comment. – Andrejs Cainikovs Jun 04 '12 at 18:59
  • See this question and its answers for "best practices": [How do I best handle dynamic multi-dimensional arrays in C/C++?](http://stackoverflow.com/questions/365782/how-do-i-best-handle-dynamic-multi-dimensional-arrays-in-c-c) – HostileFork says dont trust SE Jun 04 '12 at 19:04

5 Answers5

3

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;
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • 1
    Thanks. I was just wondering if there was a way to do it with arrays but now that I've used vectors its made my life a LOT easier. I actually decided to use a vector of 1D arrays. – user1413793 Jun 04 '12 at 21:30
2

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.

Kamran Amini
  • 1,062
  • 8
  • 14
1

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.

Israel Unterman
  • 13,158
  • 4
  • 28
  • 35
0

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.

  • 1
    Wouldn't `new *[SIZE]` be better than `malloc`? – chris Jun 04 '12 at 18:58
  • `foo(int[3][4] a)`, `foo(int[3][] a)` are both valid. (Or maybe the [][] goes after a, I can never remember.) – djechlin Jun 04 '12 at 19:10
  • @H2C03 thank you for the absolutely useless and rude comment. Yes I can read plain english but maybe you cant because you completely misread my question. Its understandable that you were angry because you could not find an answer to what I asked or maybe you just did not understand what I asked and maybe that explains your rudeness. Thank you for wasting my time. – user1413793 Jun 04 '12 at 21:28
0

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.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
djechlin
  • 59,258
  • 35
  • 162
  • 290