C allows users to change certain behaviors of New and Delete expressions by custom Operator New and Operator Delete, which gives the programmer to customize the freedom of memory management solutions. But when you enjoy this freedom, you must follow a certain specification, you can see the Terms of Terms of "Effective C 2nd". This article addresses some questions that are particularly easy to cause misunderstandings.
Operator New and Operator Delete have its formal form:
Void * Operator new; 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); the following discussions on Operator New and Operator Delete If there is no specification, it is also suitable for their array versions.
Ordinary NEW and DELETE expressions are called in allocation and release of memory is the version of these regular forms. The second parameter in Operator Delete is automatically passed by the system to indicate the actual size of the object referred to in the pointer. Generally, the priority of Operator delete is higher than Operator delete (void *, size_t), which means that if the same space (scope defines these two forms of Delete, the single parameter is prioruously compiled Options. This is verified in VC7.1, and what other compilers do?
In addition to the above formal form, we can also define Operator New and Operator Delete with more parameters. As long as the front return value and the first parameters are Void * and SIZE_T types, the latter is Void and Void * Ok. such as:
Void * operator new (size_t size, const char * szfile, int nline); Void Operator delete (void * p, const char *, int);
Expression New ("xxx", 20) SomeClass actually tells the compiler to call the above multi-parameter Operator New to allocate memory. But don't launch DELETE ("XXX", 20) Pobj according to this class, which is illegal. So how can you call this multi-parameter Operator Delete? To tell you, you don't have this right. Oh, don't surprise, let me explain slowly. When two Operator New and Operator delete have equal number, and in addition to the type of the remaining parameters other than the first parameter, we call them a pair of Matched Operator New and Operator Delete. According to this standard, the above two is a pair of matches. In the process of building an object in the heap in the heap, we use someClass * pobj = new ("xxx", 20) SomeClass, if an exception occurs when the SomeClass constructor is executed, and this exception is captured, then the C exception handling The mechanism will automatically release the memory with the Operator delete that matches the Operator New used (supplement: Throw an exception in Operator New does not cause this action because the system considers that this marks the failed memory allocation.). During the compiler, look for the match in the following order: first look for in the class domain of the build object class, then in the parent class, final to the global domain, once you find it, you find it to stop searching and use it to generate the correct memory Release the code, if not found, the code will not be used to release the allocated memory when the above exception is happened, which causes memory leakage. And if everything is normal, Delete Pobj will always call the formal form of Operator Delete. Now understand, multi-parameter Operator Delete is not to us to call the system, it is often unknown, but in the most critical juncture, it can stand up and ensure the robustness of the program. In order to have a sense of understanding, let's take a look at the code below (the test environment is VC7.1): # include
Struct Base {base () {throw int (3);
~ Base () {}
Void * Operator new (size_t nsize, const char *, int) {void * p = malloc (nsize);
Return P;
Void Operator delete (void * p) {free (p);}
Void Operator Delete (Void * p, const char *, int) {free (p);}};
#define null 0 # Define new new news (__ file__, __line__)
INT main (void) {base * p = null;
Try {p = new base;
DELETE P;} catch (...) {}
Return 0;}
Tracking execution will find: The program immediately jumps to execute Operator delete immediately after p = new base. Note The throw int (3) in the Base constructor is rounded again, then New is successful, then executes the delete p, then the actual call is Base :: Operator Delete (void *). The above test results are in line with our expectations. Note that Operator New and Operator delete can be inherited and redefined, then look down to see their performance in the inheritance system. Introducing a Base derived class (code plus in front of #define null 0): Struct Son: public base {son () {}
Void * Operator new (size_t nsize, const char *, int) {// Class Son
Void * p = malloc (nsize); returnit p;
Void Operator Delete (Void * P) {// Class Son Free (p);
Void Operator Delete (void * p, const char *, int) {// class son free (p);}}; then change the p = new base in the main function to P = New Son and cancel the BASE () THROW INT (3) annotation, tracking execution, found that this New Expression call is the SON-redefined Operator New, and throws an exception and quickly enters the correct Operator Delete, which is the multi-parameter version of SON. Everything is as expected, is it? Oh, don't rush to the conclusion, let us take a throwing statement to comment out and run again. It is obvious, some are not right. This delete p did not call Son :: Operator Delete (void *) as we wish, but found the version defined in Base. what happened? I am willing to stay a minute to make curios.
Have you found the answer? Yes, the culprit is the stupid base destructor declaration. As a base class that leads the derived class, the destructor does not declares a Virtual function, which is simply malfane. Hurry and correct it, add a sacred Virtual, Rebuild and Run, before ~ base (). Thank you, the world is finally perfect.
Maybe you will be confused, before C does C know how to correctly call the derived class definition before you do not give the base class destructive function, not the base class? In fact, it is very simple, New an object The exact type of this object must be provided, so the compiler can determine which class defined Operator delete should be called after the New Expression throws an exception. For normal Delete P, if P is declared as a pointer to the non-base type type, the compiler will determine the Operator Delete (static binding) defined when it is compiled, and if P is a certain basis Class type pointer, the compiler will smartly call which class definition of Operator delete leaves the runtime decision (dynamic binding). So how do the compiler determine if P is the base class pointer? In fact, its dependent is the destructor defined in the declaration type of P, and only P is regarded as a base class pointer when the destructor is virtual. This can explain the problems encountered above. At that time, P was declared as base *, which actually points to a SON object, but we did not declare the ~ base () declare to virtual, so the compiler boldly helped us to make static bindings, which also generates calls Base: : Operator delete (void *) code. However, don't think that all compilers will do this, and the above analysis is only based on the performance of VC7.1 in this trial. In fact, in the C standard, a derived class object is deleted via a base class pointer, and this base class has a non-virtual destructor, and the result is not defined. Understand the old brother, the compiler did not generate the code in this case. The code for your computer is considerable, and it is quite polite. Now you can experience SCOTT Meyers to persuade you to "always let Base Class have the pain of Virtual Detructor". Finally, it is to point out that the implementation of Operator New and Operator delete is quite irregular in the test code, and the responsible practice will still refer to Scott Meyers' work.