Reputation: 4428
Suppose I have a grid of squared defined like so in a class:
Square (* grid)[];
This, oddly, seems to compile fine. I would think it would error because the compiler doesn't know how big the array is? Anyways, it means it is a pointer to an array. Then to initialize it, I do:
grid(new Square[width * height])
This isn't accepted by the compiler, because the new
statement returns a pointer to squares rather than a pointer to an array of squares. It makes sense that it does that. Now, is there a simple way to accomplish what I'm asking, other than just declaring Square ** grid
and looping through it and doing separate allocations for each column of the 2D array?
Upvotes: 0
Views: 625
Reputation: 88155
I believe the reason Square (* grid)[];
works is because pointers to incomplete types are allowed, and an array without a size counts as an incomplete type.
The reason that you can't do
Square (* grid)[] = new Square[width * height];
even though it looks like the types match up perfectly is just another manifestation of the bug in the design of C where array types are treated specially. It seems like new
ing an object of type Square[]
should return a pointer to an object of that type. However you're not really new
ing an array type, but new[]
ing the element type Square
. The result of new[]
is a pointer to the element type, in line with the C convention for arrays.
You can use a cast to 'fix' this to use the 'right' type:
// pretend array types behave rationally
Square (* grid)[] = (Square (*)[]) new Square[width * height];
(*grid)[3] = 10;
// the above is equivalent to the following
Square *grid = new Square[width * height];
grid[3] = 10;
Or you can just do it the C++ way and use a std::vector
std::vector<Square> grid(width * height);
If you have a fixed size array you can use std::array
std::array<Square,10> *grid = new std::array<Square,10>;
std::array
pretty much fixes all the mistakes made in the design of array types. For example std::array
can't 'forget' its size, functions can take std::array
parameters by value (whereas with raw arrays the array syntax just becomes a synonym for pointers), functions can return std::array
whereas they are inexplicably prohibited from returning arrays (The syntax would be int foo()[3];
), and with std::array
there's no need for a special array allocator new[]
which must be matched to an array deallocator delete[]
(instead you can say foo = new std::array<int,3>
and then 'delete foo;')
Upvotes: 0
Reputation: 254441
Square (* grid)[];
This, oddly, seems to compile fine. I would think it would error because the compiler doesn't know how big the array is?
That's declaring a pointer to an array, not an array; it's fine to declare a pointer to any incomplete type, including an array of unknown size. However, it's quite an unusual thing to do, and not what you want for a dynamic array.
Now, is there a simple way to accomplish what I'm asking?
The easiest dynamic array to use is:
std::vector<Square> grid;
initialised as
grid(width * height)
If you really want to manage the memory yourself, then change your pointer-to-array to a pointer-to-object:
Square * grid;
initialised as
grid(new Square[width * height])
A pointer can point to either a single object, or the start of an array; if it does point to an array, then you can use []
on it just like with a non-dynamic array. Make sure you deallocate it (delete [] grid;
) once you've finished with it.
If you want a 2-dimensional array, it's often easiest to use a 1-dimensional array, and wrap the necessary arithmetic in an accessor function:
Square & get_square(size_t row, size_t col) {
return grid[row * width + col];
}
Upvotes: 3