Customize new and delete to change the memory management solution

Source: Internet
Author: User
Tags define null

C ++ allows manual memory management. Based on the behavioral characteristics of memory usage, modify the allocation and return work to achieve the optimal efficiency (including time and space) of the system it builds ). This gives programmers the freedom to change the new and delete expressions by customizing operatornew and operatordelete to change the memory management solution. However, when enjoying such freedom, you must abide by certain specifications. For details, see the related provisions of license tivec ++ 2nd. On this basis, this article will explain some problems that are particularly likely to cause misunderstanding.

Operator new and operator Delete are only suitable for allocating a single object. The memory used by array is allocated by operatornew [] and returned by operatordelete. All of the following terms on operatornew and operatordelete also apply to operatornew [] and operatordelete [].

Before operator new throws an exception to reflect an unfulfilled memory requirement, it first calls the specified error handler, a so-called new_handler. The relevant function declaration is located in the header file <New>:

newspace std {                typedefvoid (*new_handler)();                new_handlerset_new_handler(new_handler p) throw();}

C ++ does not support new_handler exclusive to the class. However, you can implement this behavior on your own.

You only need to make each class provide its own set_new_handler and operatornew. Specifically, set_new_handler allows the customer to specify new_handler that specifically corresponds to class calls. As for operatornew, ensure that globalnew_handler is replaced with new_handler when the class Object Memory is allocated.

Operator new of customclass needs to do the following:

1. Call the standard set_new_handler to install new_handler of customclass as globalnew_handler.

2. Call globaloperator new to execute the actual memory allocation. If the allocation fails, globaloperator new will call new_handler of cutomclass because the function was just installed as globalnew_handler. If globaloperator new cannot allocate enough memory, a bad_alloc exception will be thrown. In this case, operatornew of customclass must restore the original globalnew_handler and then spread the exception.

3. If globaloperator new can allocate enough memory, operatornew of customclass will return a pointer pointing to the allocated income.

Void * customclass: operatornew (STD: size_tsize) Throw (STD: bad_alloc) {// resume global new_handler when installing new_handler of cusomclass. Newhandlerholder H (STD: set_new_handler (currenthandler); Return: operatornew (size );}

The combination of this mechanism is to establish a baseclass and allow derivedclasses to inherit a single specific capability-"set the class exclusive new_handler. Then convert the baseclass to template, so that each derivedclass will obtain the classdata attachment (static member variable) with different entities ).

Template <typename T> // template is used to support the class-specific set_new_handlerclassnewhandlersupport {public: static STD: new_handlerset_new_handler (STD: new_handler p) Throw (); static void * operator new (STD: size_t size) Throw (STD: bad_alloc );//... PRIVATE: static STD: new_handlercurrenthandler;}; Template <typename T> STD: new_handlernewhandlersupport <t >:: set_new_handler (STD: new_handler p) Throw () {STD :: new_handler oldhandler = currenthandler; currenthandler = P; return oldhandler:} template <typename T> void * newhandlersupport <t >:: operatornew (STD: size_t size) Throw (STD :: bad_alloc) {newhandlerhodler H (STD: set_new_handler (currenthandler); Return: operatornew (size):} // initialize each currenthandler to nulltemplate <typename T> STD :: new_handlernewhandlersupport <t>: currenthandler = 0; classcustomclass: publicnewhandlersupport <customclass> {//... // same as before, but it does not need to declare set_new_handler or operator new}

This is all the actions that customclass needs to perform in order to provide "set_new_handler exclusive class.

The following is the non-member operator new pseudo code:

Void * operatornew (STD: size_tsize) Throw (STD: bad_alloc) {using namespacestd; If (size = 0) {size = 1;} while (true) {// try to allocate size bytes; If (Allocation successful) Return (a pointer pointing to the allocated memory); // allocation failed: find the current new_handling function new_handler globalhandler = set_new_handler (0); set_new_handler (globalhandler); If (globalhandler) (* globalhandler) (); else throw STD: bad_alloc ();}}

Understand When itmakes sense to replace New and delete.

· Used to detect application errors

· To improve efficiency

· To collect statistics on the use of dynamically allocated memory

· To increase the allocation and return speed

· To reduce extra space overhead caused by default Memory Manager

· Alignment)

· To cluster related objects

· To obtain nontraditional Behaviors

Principles and Methods

When you write a placementoperator new, make sure that the corresponding placementoperator Delete is also written..If this is not done, your program may experience a slight and intermittent memory leakage. (Details later)

When you declare placementnew and placementdelete, be sure not to unconsciously conceal their normal versions.


Operatornew and operator delete both have their regular form (normal signature ):

Void * operator new (size_t size );
Void operator Delete (void * P );
Void operator Delete (void * P, size_t size );

The array version is:

Void * operator new [] (size_t size );
Void operator Delete [] (void * P );
Void operator Delete [] (void * P, size_t size );

Common New and delete expressions call these regular versions when allocating and releasing memory. The second parameter in operator Delete (void * P, size_t size) is automatically transferred by the system to indicate the actual size of the object indicated by the pointer. Generally, the priority of operator Delete (void *) is higher than that of operator Delete (void *, size_t). This means that if the two types of Delete are defined in the same scope, the compiler selects a single parameter first. This is verified in VC. I don't know how to use other compilers?

In addition to the formal form above, weYou can also define operator new and operator Delete with more parameters, as long as the return values of the former and the first parameters are void * And size_t, respectively, the latter is void and void * respectively.. For example:

Void * operator new (size_t size, const char * szfile, intnline );
Void operator Delete (void * P, const char *, INT );

Expression new ("XXX", 20) customclass actually tells the compiler to call the above multi-parameter operator new to allocate memory. But do not include Delete ("XXX", 20) pobj. This is invalid. So how can we call this multi-parameter operator delete? To tell you the truth, you do not have this right. Don't be surprised. Let me explain it slowly.

When you write a new expression to construct an object, two functions are called: operatornew for memory allocation and default for class.

Assume that the first function is successfully called, but the second function throws an exception. In this case, the memory allocation gains in step 1 must be canceled and the old view must be restored. During the runtime of C ++, the system looks for the operatornew called in step 1 to match the operatordelete version. If not, the runtime system does not know how to cancel and resume the original new call. So I did nothing.

When two operator new and operator Delete have the same number of parameters, and the types of other parameters except the first parameter are identical in sequence, we call them a pair of matched operator new and operator Delete. According to this standard, the above two are matched. When we use customclass * pobj = new ("XXX", 20) mclass to build an object in the heap, if an exception occurs when executing the customclass constructor, if this exception is captured, the C ++ Exception Handling Mechanism will automatically use the operator delete that matches the operator new to release the memory (to add one point: throwing an exception in operatornew does not result in such an action, because the system determines that the memory allocation fails ).

During compilation, the compiler searches for the Matched persons in the following order: first, searches for the class domains of the constructed object class, then goes to the parent class domain, and finally to the full local area, in this process, once found, the search is stopped and used to generate the correct memory release code. If not found, no code will be used to release the allocated memory when the above exception occurs, this causes memory leakage. And if everything is normal,Delete pobj always calls the regular form of operator Delete.

Now you understand,Operator Delete with multiple parameters is not for us but for the system.It is usually unknown, but can stand up at the most critical moment to ensure the robustness of the program. To have a perceptual knowledge, let's look at the following code (the test environment is VC ):

#include <malloc.h> struct Base{    Base()    {        throw int(3);    }     ~Base() {}     void* operatornew( size_tnSize, constchar*pFile, int lineNum)    {        void* p= malloc( nSize );         returnp;    }     void operatordelete( void *p)    {        free(p);    }     void operatordelete( void* p, const char*, int)    {        free( p );    }}; #define NULL0#define new new(__FILE__,__LINE__) int main(void ){    Base* p = NULL;     try    {        p = newBase;         deletep;    }    catch(...)    {    }     return 0;}

Tracking and execution will find that the program immediately jumps to execute operator Delete (void *, const char *, INT) after throwing an exception at P = new base ). Comment out the throw int (3) in the base constructor and repeat it again. Then, new is successful and delete p is executed. Base: Operator Delete (void *) is actually called *). The above test results meet our expectations. Note that operator new and operator Delete can be inherited and redefined, so let's take a look at their performance in the inheritance system. Introduce a base derived class (code is added before # define null 0 ):

struct Son :public Base{    Son()    {    }     void* operatornew( size_tnSize, constchar*,int)    {        // class Son         void* p= malloc( nSize );        returnp;    }     void operatordelete( void *p)    {        // class Son        free(p);    }     void operatordelete( void* p,constchar*,int)    {        // class Son        free( p );    }};

Then, change p = new base in the main function to P = new son and cancel the comments on throw int (3) in base (), and trace and execute, it is found that the new expression calls the son-defined operator new. After an exception is thrown, it quickly enters the correct operator Delete, that is, the son-defined multi-parameter version. Everything is as expected, right? Well, don't rush to conclusions. Let's comment out the exception-throwing statement and run it again. Obviously, something is wrong. This Delete P does not call son: Operator Delete (void *) as we wish, but finds the version defined in base. What's going on? I 'd like to leave a minute to make you curious.

Have you found the answer? That's right. The culprit is the stupid base destructor declaration. As a base class that leads a derived class, the Destructor cannot be declared as a virtual function. Hurry up and correct it ~ Add a sacred virtual, rebuild and run... before base ...... Thank God, the world is perfect.

You may wonder why C ++ correctly calls the multi-parameter operator Delete defined by the derived class when an exception occurs before the basic class destructor is added to virtual, instead of the base class? In fact, it is very simple. When a new object is created, it must provide the exact type of the object. Therefore, the compiler can determine which class of operator Delete should be called after the new expression throws an exception during compilation. For normal delete p, if p is declared as a non-base class pointer, the compiler will decide to call the operator Delete (static binding) defined in the Declaration type during compilation ), if P is a base class type pointer, the compiler will intelligently leave the operator delete of the class to be called for Runtime (dynamic binding ). How does the compiler determine whether P is a base class pointer? In fact, it is based on the Destructor (rtti) defined in the p declaration type. P is regarded as a base class pointer only when the Destructor are virtual. This explains the problems encountered above. When P was declared as base *, it actually points to a son object in the program, but we didn't ~ The base () is declared as virtual, so the compiler makes a static binding for us, that is, generating code that calls Base: Operator Delete (void. However, do not think that all compilers will do this. The above analysis is only based on the performance of Vc in this experiment. In fact, in the C ++ standard, a base class pointer is used to delete a derived class object, but this base class has a non-virtual destructor and the result is undefined. Understand, dude. In this case, the compiler does not generate the code to detonate your computer. It is very polite and responsible. Now you should be able to understand Scott Meyers's painstaking efforts to "always give the base class a virtual destructor.

Finally, we should point out that the implementation of operator new and operator Delete in the test code is quite nonstandard. Please refer to Scott Meyers for the responsible practices.


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.