C + + Proverbs: Understanding the behavior of New-handler

Source: Internet
Author: User
Tags class definition what operator

When operator new fails to satisfy a memory allocation request, it throws a exception (exception). Long ago, he returned a null pointer (null pointer), while some older compilers were still doing so. You can still reach the previous goal (to some extent), but I'm going to discuss it at the end of this article.

Before operator new throws a exception in response to an unsatisfied memory request, it first invokes a error-handling function (error handler) that can be specified by the client, called New-handler. (That's not exactly what operator new really does, it's a little more complicated than this, and the details will be discussed in the next article.) In order to specify the Out-of-memory-handling function, the client invokes set_new_handler--a standard library function declared in <new>:

Namespace Std {
typedef void (*new_handler) ();
New_handler Set_new_handler (New_handler p) throw ();
}


As you can see, New_handler is a typedef of pointers that point to functions that do not get and return anything, and Set_new_handler is a function that obtains and returns a New_handler. (the "throw ()" At the end of the Set_new_handler declaration is a exception specification (Exception specification). It basically says that this function does not throw any exceptions, although the truth is more interesting. For details, see the C + + Maxim: Code for exceptional security. )

The Set_new_handler parameter is a pointer to a function that should be called when operator new cannot allocate the requested memory. The return value of Set_new_handler is a pointer to a function that is a valid target before Set_new_handler is called.

You can use set_new_handler like this:

function to call if operator new can ' t allocate enough memory
void Outofmem ()
{
Std::cerr << "Unable to satisfy request for memory\n";
Std::abort ();
}
int main ()
{
Std::set_new_handler (OUTOFMEM);
int *pbigdataarray = new int[100000000l];
...
}


If operator new cannot allocate space for 100,000,000 integers, Outofmem will be called, and the program will abort after an error message is issued. (Incidentally, consider if you are writing this error message to Cerr ... The process in which memory must be dynamically allocated will occur. )

When operator new fails to satisfy a memory request, it repeatedly calls the New-handler function until it can find enough memory. But this high-level description is enough to deduce that a well-designed new-handler function must do one of the following things:

· Make more memory available (makes more RAM available). This could make operator new the next memory allocation attempt successful. One way to implement this strategy is to allocate a large chunk of memory when the program starts, and then release it for use when the New-handler is first invoked.

· Install a different new-handler (installs a different new-handler). If the current new-handler cannot make more memory available, perhaps it knows there is a different new-handler can do. If so, the current new-handler can install another new-handler (by calling Set_new_handler) in its own location. operator new calls the New-handler function next time, it gets the one that was recently installed. (a change in this main line is to let a new-handler change its own behavior, so that the next time it is called, you can do something different.) One way to do this is to let New-handler change the static (static), Namespace-specific (namespace-specific) or global data that can affect new-handler behavior. )

· Deinstall the New-handler (unload New-handler), that is, pass the null pointer to Set_new_handler. No New-handler is installed, and operator new throws an exception when the memory allocation is unsuccessful.

· Throw an exception (throws an exception), type Bad_alloc, or other type that inherits from Bad_alloc. Such exceptions are not captured by operator new, so they will be propagated to the place where memory requests are made.

· Not return (no longer returned), typically calling abort or exit.

These choices give you a great deal of flexibility in achieving new-handler functions.

Sometimes you may want to handle memory allocation failures in different ways, depending on the object being assigned:

Class X {
Public
static void OutOfMemory ();
...
};
Class Y {
Public
static void OutOfMemory ();
...
};
x* p1 = new X; If allocation is unsuccessful,
Call X::outofmemory

y* P2 = new Y; If allocation is unsuccessful,
Call Y::outofmemory


C + + has no support for class-specific new-handlers, but it is not required. You can do it yourself. You just have to let each class provide its own version of Set_new_handler and operator new. Class Set_new_handler allows customers to specify New-handler for this class (just as standard set_new_handler allows customers to specify global New-handler). Class's operator new ensures that class-specific New-handler is used instead of global new-handler when allocating memory for class objects.

Suppose you want to handle memory allocation failures for the Widget class. You must be aware that when operator new cannot allocate enough memory for a Widget object, you need to declare a static member (static member) of the New_handler type that points to the new-hand of the class. Ler function. The Widget looks like this:

Class Widget {
Public
Static Std::new_handler Set_new_handler (Std::new_handler P) throw ();
static void * operator new (std::size_t size) throw (Std::bad_alloc);
Private
Static Std::new_handler Currenthandler;
};


Static class members must be defined outside the class definition (unless they are const and integral), so:

Std::new_handler Widget::currenthandler = 0; init to NULL in the class
Impl. File


The Set_new_handler function in the Widget saves any pointers that are passed to it, and it returns any pointers that were saved on the previous call, which is what the standard version of Set_new_handler does:

Std::new_handler Widget::set_new_handler (Std::new_handler P) throw ()
{
Std::new_handler Oldhandler = Currenthandler;
Currenthandler = p;
return oldhandler;
}

Eventually, the Widget's operator new will do the following things:

The

invokes the standard set_new_handler for the error-handling function of the Widget. This installs the New-handler of the Widget as global New-handler.  

calls the global operator new for true memory allocation. If the allocation fails, global operator new calls the Widget's new-handler, because that function was just installed as global new-handler. If global operator new finally fails to allocate memory, it throws a Bad_alloc exception. In this case, the Widget's operator new must revert to the original global New-handler and then propagate that exception. To ensure that the original New-handler is always restored, the Widget treats the global New-handler as a resource and follows the recommendations in the C + + Proverbs: Using Object Management resources, using the resource-managing Objects (Resource management object) to prevent resource leaks (resource leaks).  

If global operator new can allocate enough memory for a widget object, the widget's operator new returns a pointer to the allocated memory. object's destructor (destructor) for managing global New-handler automatically restores the global New-handler to the state before the calling Widget's operator new. &NBSP

Here's how you can express everything in C + +. We start with the Resource-handling class, with the addition of basic RAII operations (the resources are obtained during the construction process and released during the destruction process) ("C + + Proverbs: Using Object Management Resources"), there is nothing more:

Class Newhandlerholder {
Public
Explicit Newhandlerholder (Std::new_handler NH)//Acquire current
: Handler (NH) {}//New-handler

~newhandlerholder ()//release it
{Std::set_new_handler (handler);}
Private
Std::new_handler handler; Remember it

Newhandlerholder (const newhandlerholder&); Prevent copying
newhandlerholder&//(see "C + + Proverbs: Careful consideration of copy behavior of resource management classes")
operator= (const newhandlerholder&);
};


This makes the Widget's operator new implementation very simple:

void * Widget::operator new (std::size_t size) throw (Std::bad_alloc)
{
Newhandlerholder//install Widget ' s
H (Std::set_new_handler (Currenthandler)); New-handler

Return:: operator new (size); Allocate memory
or throw

}//Restore Global
New-handler


The Widget's customers use its new-handling capabilities (the ability to process new) like this:

void Outofmem ();//Decl. O f func. To call if Mem. Alloc.
//For Widget objects fails

Widget::set_new_handler (OUTOFMEM);//Set Outofmem as Widget ' s
//new-handling function

Widget *pw1 = new Widget;//If memory allocation
//fails, call Outofmem

std::string *ps = new std::string; If memory allocation fails,
//Call the Global new-handling
//function (if there is one)

Widget::set_new_ Handler (0); Set the Widget-specific
//new-handling function to
//nothing (i.e., null)

Widget *pw2 = new Widget;//I F Mem. Alloc. Fails, throw an
//exception immediately. (There is
//no new-handling function for
//Class Widget.)


No matter what class is, the code that implements this scheme is the same, so reusing it elsewhere is a reasonable goal. An easy way to make it possible is to create a "Mixin-style" base class ("mixed-style" base class), that is, a design that allows derived classes (derived classes) to inherit a single-specific capability (in the current case, it is to set a Class-specific New-handler's ability to base class. Then turn this base class (base) into a template, so that you get different copies of class data for each inheriting class (inherited classes).

The base class section of this design allows derived classes (derived classes) to inherit the Set_new_handler and operator new functions that they all need, and this design template section ensures that each The inheriting Class (inherited classes) gets a different Currenthandler data member (a member of the database). This may sound a bit complicated, but the code looks reliable and familiar. In fact, the only real difference is that it can now be used on any class that needs it:

Template<typename t>//"Mixin-style" base class for
Class newhandlersupport{
Class-specific Set_new_handler
Public://Support

Static Std::new_handler Set_new_handler (Std::new_handler P) throw ();
static void * operator new (std::size_t size) throw (Std::bad_alloc);

...//other versions of Op. New
Private
Static Std::new_handler Currenthandler;
};

Template<typename t>
Std::new_handler
Newhandlersupport<t>::set_new_handler (Std::new_handler P) throw ()
{
Std::new_handler Oldhandler = Currenthandler;
Currenthandler = p;
return oldhandler;
}

Template<typename t>
void* newhandlersupport<t>::operator New (std::size_t size)
Throw (Std::bad_alloc)
{
Newhandlerholder h (Std::set_new_handler (Currenthandler));
Return:: operator new (size);
}
This initializes all currenthandler to null
Template<typename t>
Std::new_handler Newhandlersupport<t>::currenthandler = 0;


With this class template, adding Set_new_handler support to Widgets is easy: widgets only need to inherit from newhandlersupport<widget>. (It may seem strange, but I'll explain more details below.) )

Class Widget:public Newhandlersupport<widget> {
...//as before, but without declarations for
}; Set_new_handler or operator new


These are all the widgets need to do to provide a class-specific set_new_handler.

But maybe you're still worrying about inheriting widgets from Newhandlersupport<widget>. If so, you might be more annoyed when you notice that the Newhandlersupport template never used its type parameter T. It doesn't need to do that. All we need is to provide a different newhandlersupport--for each class that inherits from Newhandlersupport, especially its static data member (static member) currenthandler-- The copy. Template parameter T is just to differentiate one inheriting class from another. The template mechanism automatically generates a Currenthandler copy of each of the newhandlersupport that are instantiated.

For the widget to inherit from a templatized base class that treats the widget as a type parameter (the types parameter), if the concept makes you a little confused, don't feel uncomfortable. It initially had this effect on everyone. However, it has developed into such a useful technique that it has a name, although it normally seems to reflect the fact that it is not the first time they look at it. It is called the curiously recurring template pattern (the peculiar recursive templating mode) (CRTP). It's true.

At this point, I published an article suggesting a better name called "Do It for Me", because when the widget inherits from Newhandlersupport<widget>, it actually says: "I'm the widget, and I'm going to The Widget's Newhandlersupport class inherits. "No one uses my proposed name (or even myself), but one way to think of CRTP vehicular" Do it for me "might help you understand what templatized inheritance (templated inheritance) is doing.

Templates such as Newhandlersupport makes it easy to add a class-specific New-handler to any required class. However, Mixin-style inheritance (mixed style inheritance) always leads to multiple inheritance (multiple inheritance), and before we go down this path, you need to read the C + + Proverbs: Using multiple inheritance sparingly.

Until 1993, C + + required operator new to return NULL when it was unable to allocate the requested memory. Operator new is now specified to throw a Bad_alloc exception, but many C + + programs are written before the compiler starts to support the revision standard. The C + + Standardization committee does not want to abandon the code base of these test-for-null (test null), so they provide another alternative form of operator new to provide traditional failure-yields-null (failure causes NULL) behavior. These forms are referred to as "nothrow" forms, partly because they use the Nothrow objects (defined in header file <new>) where new is used:

class Widget {...};
Widget *pw1 = new Widget;//throws Bad_alloc if
//allocation fails

if (pw1 = = 0) ...//This test must FAI L

Widget *pw2 =new (std::nothrow) widget;//returns 0 if allocation for
//The widget fails

if (pw2 = = 0 ) ...//This test may succeed


For exceptions, Nothrow new provides a less-than-initially-apparent enforcement guarantee. Two things happen in the expression "new (Std::nothrow) Widget." First, the nothrow version of operator New is called to allocate enough memory for a Widget object. If this allocation fails, it is well known that operator new returns null pointer. However, if it succeeds, the Widget constructor is called, and at the moment all bets are invalidated. Widget constructor can do whatever it wants to do. It may itself new out some memory, and if it does, it is not forced to use nothrow new. Then, although the operator new called in the new Std::nothrow widget will not be thrown, the Widget constructor can. If it does, exception is spread as usual. Conclusion? Using nothrow new only guarantees that operator new will not be thrown, and there is no guarantee that an expression like the "new (Std::nothrow) Widget" will never cause a exception. In all likelihood, you'd better never need nothrow new.

Whether you are using "normal" (that is, exception-throwing) new, or its slightly smaller cousins, it is important to understand the behavior of New-handler because it can be used in two forms.

Things to Remember

Set_new_handler allows you to specify a function that can be called when a memory allocation request cannot be satisfied.

Nothrow New has a limited role because it only applies to memory allocations, and subsequent constructor calls may still throw exceptions.

http://blog.csdn.net/passion_wu128/article/details/9154503

C + + Proverbs: Understanding the behavior of New-handler

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.