How should I write ISO C++ standard conformant custom new and delete operators?
This is in continuation of Overloading new and delete in the immensely illuminating C++ FAQ, Operator overloading, and its follow-up, Why should one replace default new and delete operators?
Section 1: Writing a standard-conformant new operator
- Part 1: Understanding the requirements for writing a custom
newoperator - Part 2: Understanding the
new_handlerrequirements - Part 3: Understanding specific scenario requirements
Section 2: Writing a standard-conformant delete operator
Note: This is meant to be an entry to Stack Overflow’s C++ FAQ. If you want to critique the idea of providing an FAQ in this form, then the posting on meta that started all this would be the place to do that. Answers to that question are monitored in the C++ chatroom, where the FAQ idea started out in the first place, so your answer is very likely to get read by those who came up with the idea.
Note: The answer is based on learnings from Scott Meyers’ More Effective C++ and the ISO C++ Standard.
Part I
This C++ FAQ entry explained why one might want to overload
newanddeleteoperators for one’s own class. This present FAQ tries to explain how one does so in a standard-conforming way.Implementing a custom
newoperatorThe C++ standard (§18.4.1.1) defines
operator newas:The C++ standard specifies the semantics that custom versions of these operators have to obey in §3.7.3 and §18.4.1
Let us summarize the requirements.
Requirement #1: It should dynamically allocate at least
sizebytes of memory and return a pointer to the allocated memory. Quote from the C++ standard, section 3.7.4.1.3:The standard further imposes:
This gives us further important requirements:
Requirement #2: The memory allocation function we use (usually
malloc()or some other custom allocator) should return a suitably aligned pointer to the allocated memory, which can be converted to a pointer of an complete object type and used to access the object.Requirement #3: Our custom operator
newmust return a legitimate pointer even when zero bytes are requested.One of the evident requirements that can even be inferred from
newprototype is:Requirement #4: If
newcannot allocate dynamic memory of the requested size, then it should throw an exception of typestd::bad_alloc.But! There is more to that than what meets the eye: If you take a closer look at the
newoperator documentation (citation from standard follows further down), it states:To understand how our custom
newneeds to support this requirement, we should understand:What is the
new_handlerandset_new_handler?new_handleris a typedef for a pointer to a function that takes and returns nothing, andset_new_handleris a function that takes and returns anew_handler.set_new_handler‘s parameter is a pointer to the function operator new should call if it can’t allocate the requested memory. Its return value is a pointer to the previously registered handler function, or null if there was no previous handler.An opportune moment for an code sample to make things clear:
In the above example,
operator new(most likely) will be unable to allocate space for 100,000,000 integers, and the functionoutOfMemHandler()will be called, and the program will abort after issuing an error message.It is important to note here that when
operator newis unable to fulfill a memory request, it calls thenew-handlerfunction repeatedly until it can find enough memory or there is no more new handlers. In the above example, unless we callstd::abort(),outOfMemHandler()would be called repeatedly. Therefore, the handler should either ensure that the next allocation succeeds, or register another handler, or register no handler, or not return (i.e. terminate the program). If there is no new handler and the allocation fails, the operator will throw an exception.Continuation 1