You rarely need to provide a custom
new or
delete , but if you need one you usually need both. If you de fine a class-specific
T::operator new to do some special allocation, it's very likely you need to define a class-specific
T::operator delete as well to do the corresponding special deallocation.
That much may be somewhat basic, but there is a subtler reason for this Item: The compiler might be yearning for an overload of
T::operator delete even when you never actually invoke it. That's why you always need to provide
operator new and
operator delete (and
operator new[] and
operator delete[] ) in pairs.
Say you define a class with customized allocation:
class T {
// … static void* operator new(std::size_t); static void* operator new(std::size_t, CustomAllocator&); static void operator delete(void*, std::size_t); };
You establish a simple protocol for allocation and deallocation:
Callers can allocate objects of type
T with either the default allocator (using
new T ) or the custom allocator (using
new(alloc) T where
alloc is an object of type
CustomAllocator ).
The only
operator delete that callers can ever invoke is the default
operator delete(size_t) , so of course you implement such that it correctly deallocates memory allocated either way.
So far, so good.
However, the compiler still needs to covertly call another overload of
delete , namely
T::operator delete(size_t, CustomAllocator&) . This is because the statement
T* p = new(alloc) T;
really expands into something like
// compiler-generated code for T* p = new(alloc) T;
// void* __compilerTemp =
T::operator new(sizeof(T), alloc) ; T* p; try { p =
new (__compilerTemp) T ;
// construct a T at address __compilerTemp } catch(...) {
// constructor failed, attention here…
T::operator delete(__compilerTemp, sizeof(T), alloc); throw; }
So, quite logically, the compiler automatically inserts code to call the corresponding
T::operator delete for the overloaded
T::operator new if the allocation succeeds but the constructor fails. The "corresponding" signature is
void operator delete(void*,
whatever-parameters-new-takes
) .
Here comes the fly-in-the-ointment part. The C++ Standard (in [C++03] §5.3.4(17)) specifies that the code above will be generated if and only if that overload of
operator delete actually exists. Otherwise, the code does not invoke any
operator delete at all in the case of a constructor failure. In other words: If the constructor fails, memory
will leak. Of six popular compilers tested at the time of this writing, only two issued a warning in such situations.
That's why every overload
void* operator new(
parms
) must be accompanied by its corresponding overload
void operator delete(void*,
parms
) because the compiler wants to call them itself.