operator new
should contain an infinite loop trying to allocate memory, should call the new-handler if it can’t satisfy a memory request, and should handle requests for zero bytes; class-specific versions should handle requests for larger blocks than expected. operator delete
should do nothing if passed a pointer that is null; class specific versions should handle blocks that are larger than expected.
When write our own versions of operator new
and operator delete
, there are conventions we must follow.
1. operator new
Implementing a conformant operator new
requires:
- having the right return value
- calling the new-handling function when insufficient memory is available (item 49)
- being prepared to cope with requests for no memory
- avoiding hiding the “normal” form of
new
(item 52)
Have the right return value
If succeeding to supply the requested memory, return a pointer to it.
Call the new-handler
If failing to supply the requested memory, follow the rule in item 49 to call the new-handling function after each fauilure. It is assumed that new-handling function might be able to free up some memory. Throw an exception of type bad_alloc
only when the pointer to the new-handler is null.
Cope with request for no memory
C++ requires that operator new
return a legitimate pointer even when zero bytes are requested (because this simplifies things else where in the language), so pseudocode for a non-member operator new
looks like this:
|
|
Avoid hiding the “normal” form of new
Class specific operator new
member functions are inherited by derived classes, so it is possible that the operator new
tuned for objects of size sizeof(base)
might accidentally be called to allocate memory for an object of a derived class. To handle this situation, do following trick:
|
|
Note that sizeof(Base)
can never be zero - freestanding objects have non-zero size (item 39) - so the zero-size memory request will be forwarded to ::operator new
to handle in a reasonable fashion.
Control memory allocation for operator new[]
To control allocation for arrays on a per-class basis, we need to implement operator new[]
- all we can do is allocating a chunk of raw memory. The point here is that we can not do any assumption about the as-yet-nonexistent objects in the array:
- it is possible to allocate memory for an array of derived class objects via base class’s
operator new[]
through inheritance, and the size of derived class objects are usually bigger than base class objects, so the number of objects in the array is no neccessarily(bytes requested) / sizeof(Base)
size_t
parameter passed tooperator new[]
may be for more memory than will be filled with objects, because dynamically allocated arrays may include extra space to store the number of array elements (item 16).
2. operator delete
C++ guarantees it’s always safe to delete the null pointer, so things are easier in terms of implementing operator delete
:
|
|
|
|
BTW, the size_t
value C++ passes to operator delete
may be incorrect if object being deleted was derived from a base class lacking a virtual destructor, which is another reason to support the argument in item 7 that operator delete
may not work correctly if virtual destructors is omitted in base classes.