Valid tive C ++ clause 49: understand New-handler's behavior

Source: Internet
Author: User

When operator new cannot meet a memory allocation requirement, an exception is thrown. If an exception is thrown to reflect an unfulfilled memory requirement, the new-handler is called first. To specify the function used to handle insufficient memory, the customer must call set-New-handler, which is a standard function library declared in <New>:

Namespace STD {
Typedef void (* new_handler )();
New_handler set_new_handler (new_handler p) Throw ();
}

Thow () is an exception details, indicating that the function will not throw any exceptions.

The set_new_handler parameter is a function to be called when the Pointer Points to operator new and cannot allocate enough memory. Returns the new_handler function that is being executed before set_new_handler is called.

Void outofmem ()
{
STD: cout <"unable to satisfy request for memory \ n ";
STD: Abort ();
}
Int main ()
{
STD: set_new_handler (outofmem );
Int * pbigdataarray = new int [distinct L];
}

A well-designed new_handler must do the following:

1. Make more memory available. The next memory allocation operation in operator new may be successful. One way is to allocate a large block of memory when the program starts execution. When new-handler is called for the first time, it is returned to the program for use.

2. install another new-handler. If the new-handler cannot obtain more available memory, it may know that another new-Handler has this capability. At present, you can install another one to replace yourself (you only need to call set_new_handler ). When operator new calls the New-handler, it will call the latest one. (One of the changes in this melody is to let New-handler modify his behavior, so he will do something different when he is called next time. For this purpose, one way is to make new-handler modify static data, namespace data, or global data that will affect the new-handler behavior .)

3. Remove New-handler. It is to pass the NULL pointer to set_new_handler. Once no new_handler is installed, operator new throws an exception when the memory allocation fails.

4. Throw an exception of bad_alloc (or derived from bad_alloc. This exception will not be captured by operator new, so it will be passed to the memory claim.

5. Do not return back. Generally, abort or exit is called.

Sometimes you want to handle memory allocation failures in different ways, depending on the class of the allocated object:

C ++ does not support Class-specific new-handler, which is not required either. You can implement this behavior on your own. You only need to make each class provide its own set_new_handler and operator new. Specifically, set_new_handler enables the customer to specify the class-specific new-handler.

Now, assume that you want to handle the memory allocation failure of the widget class.

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 members must be defined outside the class definition (unless they are const and integer ):

STD: new_handler Widget: currenthandler = 0;
STD: new_handler Widget: set_new_handler (STD: new_handler p) Throw ()
{
STD: new_handler oldhandler = currenthandler;
Currenthandler = P;
Return oldhandler;
}

The operator new of the widget does the following:

1. Call the standard set_new_handler to inform the error handling function of the widget. This time, the new_handler of the widget is installed as global new-handler.

2. Call global operator new to execute the actual memory allocation. If the allocation fails, global operator new will call the new_handler of the widget, which has just been installed as global new-handler. If the global operator new cannot allocate enough memory, a bad_alloc exception will be thrown. In this case, the operator new of the widget must restore the original global new_handler and then spread the exception. To ensure that the original new_handler can always be reinstalled back, the widget regards global new_handler as a resource and complies with clause 13 to use resource management objects to prevent resource leakage.

3. If global operator new can allocate enough memory for a widget object, operator new of the widget will return a pointer pointing to the allocated result. The widget destructor manages global new_handler, which automatically restores the global new_handler before the widget's operator new is called.

Class newhandlerholder {
Public:
Explicit newhandlerholder (STD: new_handler NH)
: Handler (NH) {}// get the current new_handler
~ Newhandlerholder ()
{
STD: set_new_handler (handler); // release it
}
PRIVATE:
STD: new_handler handler; // record
Newhandlerholder (const newhandlerholder &); // block copying
Newhandlerholder & operator = (const newhandlerholder &);
};

This makes the implementation of the widget's operator new quite simple:

Void * Widget: Operator new (STD: size_t size) Throw (STD: bad_alloc)
{
Newhandlerholder H (STD: set_new_handler (currenthandler); // install new_handler of the widget
Return: Operator new (size); // allocate memory or throw an exception.
} // Restore global new_handler

The customer of the widget should use its new_handling as follows:

Void outofmem ();
Widget: set_new_handler (outofmem );
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.
Widget: set_new_handler (0 );
Widget * pw2 = new widget; // if the memory allocation fails, an exception is thrown immediately, and new_handling is null.

The code to implement this scheme is not different from the class, so it is a reasonable idea to reuse it. A simple approach is to build a Mixin-style base class that allows Derived classes to inherit a single specific capability. Then the base class is converted to the template, so that each derived class will obtain the class data duplication with different entities.

Template <typename T>
Class newhandlersupport {
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;
};

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)
{
Newhandlerholder H (STD: set_new_handler (currenthandler ));
Return: Operator new (size );
}

The following code initializes each currenthandler to null.

Template <typename T>
STD: new_handler newhandlersupport <t >:: currenthandler = 0;

It is easy to add the set_new_handler support capability for the Widget: as long as the widget inherits from newhandlersupport <widget>.

Class Widget: Public newhandlersupport <widget> {
... // Same as before, but do not declare set_new_handler or operator new
};

In fact, you only want to use template T. It inherits from every class of newhandlersupport and has a copy of newhandlersupport with different entities (more specifically, its static member variable currenthandler ). Type parameters are only used to differentiate different Derived classes. The template mechanism automatically generates a currenthandler for each T (newhandlersupport depends on the current.

Widgets inherit from a templated base class, while the latter uses widgets as the type parameter: "weird cyclic template pattern" (curiously recurring template pattern; crtp ). Such as do it for me.

Before December 31, 1993, operator new returns NULL if it cannot allocate enough memory. The next-generation operator new throws the bad_alloc exception.

Class widget {...};
Widget * pw1 = new widget;
If (pw1 = 0)... // This test must fail, because if the allocation fails, bad_alloc is thrown.
Widget * pw2 = new (STD: nothrow) widget;
If (pw2 = 0)... // this test may be successful. If the widget fails to be allocated, 0 is returned.

Nothrow new is quite limited because it only applies to memory allocation, and subsequent constructors may throw an exception. If the constructor has something new, no one forces it to apply nothrow new again.

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.