set-new-handler allows you to specify a function to be called when memory allocation requests cannot be satisfied.
operator new can’t satisfy a memory allocation request, it will call a client-specifiable error-handling function called a
new-handler before throwing a
bad_alloc exception. To specify this out-of-memory-handling function, clients call
set_new_handler, a standard library function declared in
throw() here is an exception specification, telling us that function
set_new_handler won’t throw any exceptions (though there’s more truth, refer to item 29).
set_new_handler's parameter is the new
new_handler we want to specify, which is a pointer to the function
operator new should call if it fails to allocate the requested memory, and the return value if the previous
operator new fails to fulfill a memory request, it calls the
new_handler function repeatedly until it succeeds to find enough memory (more details in item 51), so this default behavior gives us following conclusion - a well-designed new-handler function must do one of the following:
- Make more memory available to allow the next memory allocation attempt inside
operator newto succeed1.
- Install a different new-handler that may be capable to make more memory 2. A variation is to modify the behavior of current
new-handler(via modifying static, namespace specific, or global data the affects the new-handler’s behavior)
- Deinstall the new-handler, i.e., pass the null pointer to
set_new_handler, which leads to
operator newthrowing a
- Throw an exception of type
bad_allocor some type derived from
bad_alloc, which will firstly be caught by
operator newand then propagate to the site originating the request for memory.
- Not return, typically by calling
For example, to use
new-handler per class
C++ has no support for class-specific new-handlers, so we implement this by ourselves: we have each class provide its own versions of
set_new_handler (which allows clients to specify the customized
operator new (which ensures the class-specific
new-handler is used in place of the global
new-handler when memory allocation request fails).
To make thie ensurance confirmed, this class-specific
operator new should do the following stuff:
- Call the standard
Widget's own error-handling function as the global new-handler.
- Call the global
operator newto perform the actual memory allocation. Two things may happen during this step:
- if allocation ultimately fails and a
bad_allocis thrown by the global
operator new, restore the original global new-handler, and then propagate the exception
- if allocation succeeds, return a pointer to the allocated memory, and then restore the original global new-handler prior to the call to
- if allocation ultimately fails and a
To ensure that the original new-handler is always reinstalled, we can treat the global new-handler as a resource and follow the advice of item 13 to use resource-managing objects to prevent resource leaks:
Since the behavior of setting a class-specific new-handler is the same regardless of the class, we can reuse the setting procedure by creating a “mixin-style” base class (i.e., a base class that’s designed to allow derived classes to inherit a single specific capability), so that each derived class not only inherits the
operator new functions from the base class, but also gets a different class-specific
new-handler from the template.
With this class template, adding
set_new_handler support to
Widget is easy:
Widget just inherits from
One interesting point for this design is that
Widget inheriting from a templatized base class that takes
Widget itself as a type parameter, and this technique has a straightforwart name: curiously recurring template pattern (CRTP).
Another point worth noting is that the
NewHandlerSupport template never uses its type parameter
T, because it doesn’t need to: all we need is a different copy of static data member
currentHJandler for each class inheriting from
NewHandlerSupport, and template parameter
T just makes it possible to distinguish these derived classes - the template mechanism automatically generates a copy of
currentHandler for each
T with which
NewHandlerSupport is instantiated.
Finally, let’s take a look at how to use the new-handling capabilities in
Traditional failure-yields-null alternative for
Althought now it is specified to throw a
bad_alloc exception, until 1993, C++ required that
operator new return null when it way unable to allocate the requested memory. Unwilling to abandon the traditional behavior, the C++ standardization committee provided an alternative form of
operator new called “nothrow” forms, in part because the forms employ
nothrow objects defined in the header
new (std::nothrow) Widget only guarantees that
operator new won’t throw, not the whole expression: after the nothrow versoin of
operator new succeeds to allocate enough memory for a
Widget object, and then the
Widget constructor is called, chances are that the
Widget constructor might itself new up some memory and throw, and then the exception will be propagated as usual.
Conclusion: we never have a need for nothrow new.
One way to achieve this strategy is to allocate a large block of memory at program start-up, then release it for use in the program the first time the new-handler is invoked. ↩︎
The strategy may be implemented by calling
new-handler. The next time
operator newcall the
new-handlerfunction, it will get the one most recently installed. ↩︎