Gotcha #13: Array/Initializer Confusion
Let's allocate an array of 12 integers off the heap, shall we? No problem:
int *ip = new int(12);
So far, so good. Now let's use the array. When we've finished, we'll clean up after ourselves:
for( int i = 0; i < 12; ++i )
ip[i] = i;
delete [] ip;
Notice the use of the empty brackets to let the compiler know that ip points to an array of integers instead of a single integer. Or does it?
Actually, ip points to a single integer initialized with the value 12. We've made the common typo of using parentheses instead of square brackets. Both the access within the loop (after index 0) and the deletion are illegal. However, the compiler is unlikely to catch the error at compile time. Because a pointer can refer to either a single object or an array of objects, both the index within the loop and the array deletion are perfectly correct syntactically, and we won't learn of an error until runtime.
Maybe not even then. It's illegal to access past the end of an object (although the language guarantees that you can point one element past an object), and it's illegal to delete a scalar as an array. But just because you do something illegal doesn't mean you'll get caught (think Wall Street). This code could run perfectly well on some platforms and fail at runtime on others, or exhibit flaky behavior on a particular platform, depending on runtime heap usage of a particular thread or process. The correct allocation is, of course, the following:
int *ip = new int[12];
However, the proper allocation is most probably not to allocate at all. Just use the standard library:
std::vector<int> iv( 12 );
for( int i = 0; i < iv.size(); ++i )
iv[i] = i;
// no explicit deletion . . .
The standard vector template is nearly as efficient as the hand-coded version but is safer, faster to program, and self-documenting. In general, prefer vector to low-level arrays. By the way, the same syntax problem can occur with a simple declaration as well but is usually easier to detect:
int a[12]; // array of 12 ints
int b(12); // an int initialized to 12
|