How do I allocate multidimensional arrays by new?
A: There are several ways to do this, based on how flexible you wish the array sizing to be. On one acute, if you know all the dimensions at compile-time, you can allocate statically multidimensional arrays (like in C):
class Fred { /*...*/ };
void someFunction(Fred& fred);
void manipulateArray()
{
const unsigned nrows = 10; // Num rows is compile-time constant const unsigned ncols = 20; // Num columns is a compile-time constant Fred matrix[nrows][ncols];
for (unsigned i = 0; i < nrows; ++i) {
for (unsigned j = 0; j < ncols; ++j) {
// Here's the way you access the (i,j) element:
Some Function( matrix[i][j] );
// you can securely "return" without any special delete code:
if (today == "Tuesday" && moon.isFull())
return; // Quit early on Tuesdays while the moon is full
}
}
// No explicit delete code at the ending of the function either
}
Commonly, the size of the matrix isn't known till run-time but you know that this will be rectangular. In this case you have to use the heap ("freestore"), but at least you are capable to allocate all of the elements in one freestore chunk.
void manipulateArray(unsigned nrows, unsigned ncols)
{
Fred* matrix = new Fred[nrows * ncols];
// Since we utiized a simple pointer above, we have to be VERY
// careful to ignore skipping over the delete code.
// That's why we catch all exceptions:
try {
// Here's described how to access the (i,j) element:
for (unsigned i = 0; i < nrows; ++i) { for (unsigned j = 0; j < ncols; ++j) { some Function( matrix[i*ncols + j] );
}
}
// If you wish to quit early on Tuesdays while the moon is full,
// make sure to do the delete along with ALL return paths:
if (today == "Tuesday" && moon.isFull()) {
delete[] matrix;
return;
}
...insert code here to fiddle along with the matrix...
}
catch (...) {
// make sure to do the delete while an exception is thrown:
delete[] matrix;
throw; // Re-throw the current exception
}
// Make sure to do the delete at the ending of the function too:
delete[] matrix;
}
Lastly at the other extreme, you may not even be guaranteed that the matrix is rectangular. For instance, if each row could have a distinct length, you'll have to allocate each row individually. In following function, ncols[i] is the number of columns in row number i, where i varies among 0 and nrows-1 inclusive.
void manipulateArray(unsigned nrows, unsigned ncols[])
{
typedef Fred* FredPtr;
// There will not be leak if the following throws an exception: FredPtr* matrix = new FredPtr[nrows];
// Set each of element to NULL in case there is an exception later on.
// (See comments at the top of the try block for rationale.)
for (unsigned i = 0; i < nrows; ++i)
matrix[i] = NULL;
// Since we utilized a simple pointer above, we have to be
// VERY careful to ignore skipping over the delete code.
// That's why we catch all exceptions:
try {
// Next we populate the array. If one of these throws, all
// the allocated elements will be deleted for (unsigned i = 0; i < nrows; ++i)
matrix[i] = new Fred[ ncols[i] ];
// Here's how to access the (i,j) element:
for (unsigned i = 0; i < nrows; ++i) { for (unsigned j = 0; j < ncols[i]; ++j) { someFunction( matrix[i][j] );
}
}
// If you wish to quit early on Tuesdays while the moon is full,
// make sure to do the delete along with ALL return paths:
if (today == "Tuesday" && moon.isFull()) {
for (unsigned i = nrows; i > 0; --i)
delete[] matrix[i-1]; delete[] matrix; return;
}
...insert code here to fiddle with the matrix...
}
catch (...) {
// Make sure to do the delete while an exception is thrown:
// Note down that some of these matrix[...] pointers may be
// NULL, however that's okay since it's legal to delete NULL.
for (unsigned i = nrows; i > 0; --i)
delete[] matrix[i-1];
delete[] matrix;
throw; // Re-throw current exception
}
// Make sure to do delete at the ending of the function too.
// Note down that deletion is the opposite order of allocation:
for (unsigned i = nrows; i > 0; --i)
delete[] matrix[i-1];
delete[] matrix;
}
Note down the funny use of matrix[i-1] in the deletion procedure. This prevents wrap-around of the unsigned value while i goes one step below zero.
Lastly, note down that pointers and arrays are evil. Normally it is much better to encapsulate your pointers in a class which has a safe and simple interface. The following