Deallocating array objects, not just array storage, requires a little advance planning.
Last year and early this year, I wrote a couple of articles on dynamic allocation in C and C++ emphasizing the distinction between allocating objects and allocating raw (uninitialized) storage.1,2 When a program allocates an object, it not only allocates storage for the object, but also initializes that storage with a value appropriate for the type of object that will occupy that storage. When a program just allocates storage, it leaves that storage uninitialized.
I followed those articles with one that explained the distinction between deallocating objects and deallocating storage.3 When a program deallocates an object, it releases not only the storage occupied by the object, but also any other resources the object was using.
In each of those articles, I sketched out how C++ compilers translate new-expressions into more primitive operations. I also showed how to write C code that emulates the behavior of new-expressions. However, I stalled out when I got to array delete-expressions. I also left some details out of the code for both the C++ implementation and the C emulation of array new-expressions. This month, I'll fill in most of the missing pieces.
A recap
New-expressions in C++ allocate objects. Each new-expression is conceptually, if not actually, a two-step process: (1) allocate storage for an object, and (2) initialize it. For objects of class types, initializing an object usually involves calling a constructor.
For example, suppose class widget is defined as:
class widget
{
public:
widget(); // a constructor
~widget(); // a destructor
// ...
};
Then a new-expression such as:
pw = new widget ();
translates more-or-less into something like:
pw = static_cast<widget *>(operator new(sizeof(widget)));
pw->widget();
The first statement acquires storage for a widget object by calling operator new, and converts the address of that storage from type void * to type widget *. The second statement initializes the storage by applying widget's default constructor. (That second statement--an explicit constructor call--is not something you can actually write in C++.)
Delete-expressions in C++ deallocate objects. Each delete-expression is also a two-step process: (1) release resources that the object was using, and (2) deallocate the storage for the object. For objects of class types, releasing resources involves calling a destructor.
If pw is a pointer to an object of class type widget, a delete-expression such as delete pw; translates more-or-less into something like:
if (pw != NULL)
{
pw->~widget();
operator delete(pw);
}