Item 8: Understand the different meanings of new
and delete
.
It occasionally seems as if people went out of their way to make C++ terminology difficult to understand. Case in point: the difference between the new
operator and operator
new
.
When you write code like
string *ps = new string("Memory Management");
the new
you are using is the new
operator. This operator is built into the language and, like sizeof
, you can't change its meaning: it always does the same thing. What it does is twofold. First, it allocates enough memory to hold an object of the type requested. In the example above, it allocates enough memory to hold a string
object. Second, it calls a constructor to initialize an object in the memory that was allocated. The new
operator always does those two things; you can't change its behavior in any
What you can change is how the memory for an object is allocated. The new
operator calls a function to perform the requisite memory allocation, and you can rewrite or overload that function to change its behavior. The name of the function the new
operator calls to allocate memory is operator
new
.
The operator
new
function is usually declared like
void * operator new(size_t size);
The return type is void*
, because this function returns a pointer to raw, uninitialized memory. (If you like, you can write a version of operator
new
that initializes the memory to some value before returning a pointer to it, but this is not commonly done.) The size_t
parameter specifies how much memory to allocate. You can overload operator
new
by adding additional parameters, but the first parameter must always be of type size_t
. (For information on writing operator
new
, consult Items E8-E10.)
You'll probably never want to call operator
new
directly, but on the off chance you do, you'll call it just like any other
void *rawMemory = operator new(sizeof(string));
Here operator
new
will return a pointer to a chunk of memory large enough to hold a string
Like malloc
, operator
new
's only responsibility is to allocate memory. It knows nothing about constructors. All operator
new
understands is memory allocation. It is the job of the new
operator to take the raw memory that operator
new
returns and transform it into an object. When your compilers see a statement
string *ps = new string("Memory Management");
they must generate code that more or less corresponds to this (see Items E8 and E10, as well as the sidebar to my article on counting objects, for a more detailed treatment of this
void *memory = // get raw memory operator new(sizeof(string)); // for a string // object
call string::string("Memory Management") // initialize the on *memory; // object in the // memory
string *ps = // make ps point to static_cast<string*>(memory); // the new object
Notice that the second step above involves calling a constructor, something you, a mere programmer, are prohibited from doing. Your compilers are unconstrained by mortal limits, however, and they can do whatever they like. That's why you must use the new
operator if you want to conjure up a heap-based object: you can't directly call the constructor necessary to initialize the object (including such crucial components as its vtbl see Item 24).
There are times when you really want to call a constructor directly. Invoking a constructor on an existing object makes no sense, because constructors initialize objects, and an object can only be initialized given its first value once. But occasionally you have some raw memory that's already been allocated, and you need to construct an object in the memory you have. A special version of operator
new
called placement new
allows you to do
As an example of how placement new
might be used, consider
class Widget { public: Widget(int widgetSize); ... };
Widget * constructWidgetInBuffer(void *buffer, int widgetSize) { return new (buffer) Widget(widgetSize); }
This function returns a pointer to a Widget
object that's constructed within the buffer passed to the function. Such a function might be useful for applications using shared memory or memory-mapped I/O, because objects in such applications must be placed at specific addresses or in memory allocated by special routines. (For a different example of how placement new
can be used, see Item 4.)
Inside constructWidgetInBuffer
, the expression being returned
new (buffer) Widget(widgetSize)
This looks a little strange at first, but it's just a use of the new
operator in which an additional argument (buffer
) is being specified for the implicit call that the new
operator makes to operator
new
. The operator
new
thus called must, in addition to the mandatory size_t
argument, accept a void*
parameter that points to the memory the object being constructed is to occupy. That operator
new
is placement new
, and it looks like
void * operator new(size_t, void *location) { return location; }
This is probably simpler than you expected, but this is all placement new
needs to do. After all, the purpose of operator
new
is to find memory for an object and return a pointer to that memory. In the case of placement new
, the caller already knows what the pointer to the memory should be, because the caller knows where the object is supposed to be placed. All placement new
has to do, then, is return the pointer that's passed into it. (The unused (but mandatory) size_t
parameter has no name to keep compilers from complaining about its not being used; see Item 6.) Placement new
is part of the standard C++ library (see Item E49). To use placement new
, all you have to do is #include
<new>
(or, if your compilers don't yet support the new-style header names (again, see Item E49), <new.h>
).
If we step back from placement new
for a moment, we'll see that the relationship between the new
operator and operator
new
, though
you want to create an object on the heap, use the new
operator. It both allocates memory and calls a constructor for the object. If you only want to allocate memory, call operator
new
; no constructor will be called. If you want to customize the memory allocation that takes place when heap objects are created, write your own version of operator
new
and use the new
operator; it will automatically invoke your custom version of operator
new
. If you want to construct an object in memory you've already got a pointer to, use placement new
.
(For additional insights into variants of new
and delete
, see Item E7 and my article on counting objects.)
Deletion and Memory Deallocation
To avoid resource leaks, every dynamic allocation must be matched by an equal and opposite deallocation. The function operator
delete
is to the built-in delete
operator as operator
new
is to the new
operator. When you say something like this,
string *ps; ... delete ps; // use the delete operator
your compilers must generate code both to destruct the object ps
points to and to deallocate the memory occupied by that
The memory deallocation is performed by the operator
delete
function, which is usually declared like
void operator delete(void *memoryToBeDeallocated);
delete ps;
causes compilers to generate code that approximately corresponds to
ps->~string(); // call the object's dtor
operator delete(ps); // deallocate the memory // the object occupied
One implication of this is that if you want to deal only with raw, uninitialized memory, you should bypass the new
and delete
operators entirely. Instead, you should call operator
new
to get the memory and operator
delete
to return it to the
void *buffer = // allocate enough operator new(50*sizeof(char)); // memory to hold 50 // chars; call no ctors
...
operator delete(buffer); // deallocate the memory; // call no dtors
This is the C++ equivalent of calling malloc
and free
.
If you use placement new
to create an object in some memory, you should avoid using the delete
operator on that memory. That's because the delete
operator calls operator
delete
to deallocate the memory, but the memory containing the object wasn't allocated by operator
new
in the first place; placement new
just returned the pointer that was passed to it. Who knows where that pointer came from? Instead, you should undo the effect of the constructor by explicitly calling the object's
// functions for allocating and deallocating memory in // shared memory void * mallocShared(size_t size); void freeShared(void *memory);
void *sharedMemory = mallocShared(sizeof(Widget));
Widget *pw = // as above, constructWidgetInBuffer(sharedMemory, 10); // placement // new is used
...
delete pw; // undefined! sharedMemory came from // mallocShared, not operator new
pw->~Widget(); // fine, destructs the Widget pointed to // by pw, but doesn't deallocate the // memory containing the Widget
freeShared(pw); // fine, deallocates the memory pointed // to by pw, but calls no destructor
As this example demonstrates, if the raw memory passed to placement new
was itself dynamically allocated (through some unconventional means), you must still deallocate that memory if you wish to avoid a memory leak. (See the sidebar to my article on counting objects for information on "placement delete
".)
So far so good, but there's farther to go. Everything we've examined so far concerns itself with only one object at a time. What about array allocation? What happens
string *ps = new string[10]; // allocate an array of // objects
The new
being used is still the new
operator, but because an array is being created, the new
operator behaves slightly differently from the case of single-object creation. For one thing, memory is no longer allocated by operator
new
. Instead, it's allocated by the array-allocation equivalent, a function called operator
(often referred to as "array new
.") Like operator
new
, operator
can be overloaded. This allows you to seize control of memory allocation for arrays in the same way you can control memory allocation for single objects (but see Item E8 for some caveats on
(operator
is a relatively recent addition to C++, so your compilers may not support it yet. If they don't, the global version of operator
new
will be used to allocate memory for every array, regardless of the type of objects in the array. Customizing array-memory allocation under such compilers is daunting, because it requires that you rewrite the global operator
new
. This is not a task to be undertaken lightly. By default, the global operator
new
handles all dynamic memory allocation in a program, so any change in its behavior has a dramatic and pervasive effect. Furthermore, there is only one global operator
new
with the "normal" signature (i.e., taking the single size_t
parameter see Item E9), so if you decide to claim it as your own, you instantly render your software incompatible with any library that makes the same decision. (See also Item 27.) As a result of these considerations, custom memory management for arrays is not usually a reasonable design decision for compilers lacking support for operator
.)
The second way in which the new
operator behaves differently for arrays than for objects is in the number of constructor calls it makes. For arrays, a constructor must be called for each object in the
string *ps = // call operatornew[] to allocate new string[10]; // memory for 10 string objects, // then call the default string // ctor for each array element
Similarly, when the delete
operator is used on an array, it calls a destructor for each array element and then calls operator
to deallocate the
delete [] ps; // call the string dtor for each // array element, then call // operatordelete[] to // deallocate the array's memory
Just as you can replace or overload operator
delete
, you can replace or overload operator
. There are some restrictions on how they can be overloaded, however; consult a good C++ text for details. (For ideas on good C++ texts, see the recommendations beginning on page 285.)
So there you have it. The new
and delete
operators are built-in and beyond your control, but the memory allocation and deallocation functions they call are not. When you think about customizing the behavior of the new
and delete
operators, remember that you can't really do it. You can modify how they do what they do, but what they do is fixed by the