Effective C ++ 2e Item7

zhaozj2021-02-11  215

Terms 7: Pre-prepared in advance

Operator New will throw an exception when you can't complete memory allocation (former practice is usually returned 0, some older compilers do this. If you want, you can set your compiler to this. About this topic The discussion will be discussed at the end of this Territor. Everyone knows that it is really a moral behavior that the abnormality that deals in memory is really a moral behavior, but it actually acts like a knife holder in the neck. So, sometimes you don't deserve it, maybe I have never deserved it. But your heart must still have a deep hidden sense of sin: What should I do if NEW really produces an abnormally?

You will naturally think of a method of dealing with this situation, that is, go back to the previous old road and use pretreatment. For example, a common practice for c is to define a type-independent macro to allocate memory and check whether allocation is successful. For C , this macro looks like this: #define new (ptr, type) / try {(ptr) = new type;} / catch (std :: bad_alloc &) {assert (0);} ("slow What is it? "You will ask. BAD_ALLOC is how the Operator New does not meet the amount of exceptions thrown when the memory allocation request, STD is the name of the name space of Bad_alloc (see Terms 28)." Okay! " You will continue to ask, "What is the use of Assert?" If you look at the standard C header file (or use it equivalent to the version space of the name space , see Terms 49) Discover Assert is a macro. This macro checks to its expression is non-zero. If it is not a non-zero value, it will issue an error message and call Abort.assert just when the standard macro nDebug is not defined, that is, in debug state. That did it. In the product release state, when ndebug is defined, Assert does not do, it is equivalent to an empty statement. So you can only check the assertion when debugging (Assertion).).

New Macro not only has the cases mentioned above, that is, using Assert to check the status in the published program (however, there may be no memory at any time), and it is still another shortcoming in C : it There is no consideration in New Ways. For example, want to create type T objects, there are usually three common grammatical forms, you must process an exception that may be generated for each form: New T;

New T (constructor arguments);

New t [size];

The problem is greatly simplified here because some people will also customize (overload) Operator New, so the program will contain any syntax form using the New.

So what should I do? If you want to use a very simple error handling method, you can do this: When the memory allocation request cannot meet, call your pre-specified error handling function. This method is based on a routine, that is, when the Operator New cannot meet the request, call the customer's specified error handler before thrown an exception - generally referred to as a new-handler function. (Operator New is actually complex, see clause 8)

When you specify an error handling function, you should use the set_new_handler function, which is roughly like this in the header file : typedef void (* new_handler) (); new_handler set_new_handler (new_handler p) throw (); can be seen, New_handler is a custom function pointer type, which pointing a function that does not return value without input parameters. Set_new_handler is an input and returns a function of the new_handler type.

The input parameter of the set_new_handler is the pointer to the error handler to call when the Operator NEW assigns memory failure, and the return value is the pointer of the old error handler that is already active before the set_new_handler is not called.

You can use the SET_NEW_HANDAL: / / Function to Call if Operator New Can't Allocate ENOUGH MEMORYVOID NOMOREMEMORY () {CERR << "Unable to satisfy Request for Memory / N"; Abort ();

INT main () {set_new_handler (nomorememory);

INT * PBIGDATAARRAY = New int [100000000];

...

}

If the Operator New cannot be a 100,000,000 integration space, NomoreMemory will be called, and the program is terminated after an error message. This is better than simply generating error messages to generate error messages in the system kernel. (By the way, if the CERR must be dynamically allocated in the process of writing the error message, what will happen ...)

When the Operator New cannot meet the memory allocation request, the New-handler function is not only called once, but is constantly repeating until enough memory is found. Realizing duplicate calls can be seen in terms 8, here I use a descriptive language: a well-designed New-Handler function must implement one of the following functions. · Generate more available memory. This will enable the attempt to assign memory in Operator New to be successful. One way to implement this strategy is to assign a large memory block when the program starts, and then releases when the new-handler is called for the first time. Along with some warning information to the user, if there is too little memory, the next request may fail unless there are more free space. · Install another different new-handler function. If the current New-Handler function does not produce more available memory, it may know that another new-handler function can provide more resources. In this case, the current new-handler can install another new-handler to replace it (by calling set_new_handler). The next time the Operator New is called New-handler, the most recently installed is used. (Another way to change this strategy is to let New-Handler change its own run behavior, then it will make different things when calling, it is to make New-Handler to modify the static status that affects its own behavior. Or global data.) · Remove New-Handler. That is, pass the empty pointer to set_new_handler. No new-handler is installed, the Operator New assigns memory that will throw a standard STD :: Bad_alloc type. • Throw std :: bad_alloc or other types of exceptions inherited from std :: bad_alloc. Such an exception will not be captured by the Operator New, so they will be sent to the place originally performed. (Throw other different types of exceptions will violate the Operator New anomalous specification. The default behavior in the specification is to call Abort, so when New-handler wants to throw an exception, be sure it is from std :: bad_alloc inherited from std :: bad_alloc Want to learn more about the exception regulations, see the Terms M14.) · Nothing returned. Typical practice is to call Abort or Exit. Abort / EXIT can be found in a standard C library (there are standard C libraries, see Terms 49). The above choice gives you great flexibility in the New-Handler function.

What is the method taken when handling a memory allocation failure, depends on the class of the object to be assigned: Class X {public: static void outofmemory ();

...

}

Class y {public: static void outofmemory ();

...

}

X * p1 = new x; // If the assignment is successful, call x :: outofmemory

Y * p2 = new y; // If the assignment is unsuccessful, call Y :: OutofMemory

C does not support the NEW-handler function specifically for classes, and it is not necessary. You can implement it yourself, just provide your own version of SET_NEW_HANDAL and OPERATOR New in each class. The class set_new_handler can specify new-handler for class (like a standard set_new_handler to specify the global new-handler). The Operator New guarantees the NEW-handler that replaces the global new-handler when assigning memory-type objects.

Assume that the processing class X memory allocation failed. Because the Operator New is assigned memory failure to the type X, the error handler must be called, so you have to declare a static member of a new_handler type in the class. Then the class x looks like this: Class X {public: static new_handler set_new_handler (new_handler p); static void * operator new (size_t size); private: static new_handler currenthandler;};

The static members of the class must define outside the class. Because I want to borrow the default initialization value of the static object, I don't start to initialize when defined x :: currenthandler. New_handler x :: currenthandler; // Default set currenthandler is 0 (ie null)

The set_new_handler function in class X saves any pointer to it and returns any pointer saved before calling it. This is the standard version of SET_NEW_HANDAL: new_handler x :: set_new_handler (new_handler p) {new_handler = currenthandler; currenthandler = p; returna}

Finally, look at the Operator New: 1. Call the standard set_new_handler function, enter the error handler of the parameter is x. This makes X's New-Handler function becomes a global new-handler function. Note that in the following code, the "::" symbol explicitly references the STD space (the standard set_new_handler function exists in the STD space).

2. Call the global operator new allocation of memory. If the first assignment fails, the global Operator New will call the X-Handler because it is just (see 1.) is installed as a global new-handler. If the global Operator New is finally failed to be assigned to memory, it throws std :: bad_alloc exception, X's Operator New will capture it. X of the Operator New then restores the originally replaced global new-handler function, and finally returns an exception.

3. Assume that the global operator new is allocated in memory, and the Operator New will call the criteria set_new_handler again to restore the original global error handling function. Finally, returns a pointer to the successful memory.

C is doing this: void * x :: operator new (size_t size) {new_handler globalhandler = // Install X NEW_HANDLER std :: set_new_handler (CurrentHandler);

Void * Memory;

Try {// Try allocate memory Memory = :: operator new (size);} catch (std :: bad_alloc &) {// Restore old new_handler std :: set_new_handler; throw; // Throw anomalous}

Std :: set_new_handler (globalhandler); // Restore the old new_handler return memory;} If you repeat the std :: set_new_handler to see the Terms M9, see the Terms M9.

The use of class X's memory allocation processing function is approximately as follows: void NomoreMemory (); // x object assigns the // new_handler function called when the memory fails ///

X :: set_new_handler (nomorememory); // Set NomoreMemory to x // new-handling function

X * px1 = new x; // If the memory allocation failed, // call NomoreMemory

String * ps = new string; // If the memory allocation failed, // call the global new-handling function //

X :: set_new_handler (0); // Set X's new-handling function is empty //

X * px2 = new x; // If the memory allocation fails, immediately throw an exception / (Class X No new-handling function) //

You will notice that the above similar situation, if you don't consider the class, the code is the same, which naturally thinks that you can reuse them elsewhere. As explained in Terms 41, inheritance and templates can be used to design reusable code. Here, we combine both methods to meet your requirements.

As long as you create a base class of "Mixin-style), this base class allows subclasses to inherit its specific function - here is the function of establishing a class of New-handler. The reason for designing a base class is to allow all subclasses to inherit the set_new_handler and Operator New feature, and the design template is to make each subclass have different CurrentHandler data members. This sounds complicated, but you will see the code is actually very familiar. The difference is that it can now be reused by any class. Template // Provides class new-namesupport {// "Mixed Style" for class set_new_handler PUBLIC:

Static new_handler set_new_handler (new_handler p); static void * operator new (size_t size); private: static new_handler currenthandler;};

Template New_Handler NewHandlersupport :: SET_NEW_HANDLER (new_handler p) {new_handler = purrenthandler; currenthandler = p; returna}

Template Void * NewHandlersupport :: Operator New (size_t size) {new_handler globalhandler = std :: set_new_handler (currenthandler);

Void * Memory;

Try {memory = :: Operator new (size);} cat :: set_new_handler (globalhandler);

std :: set_new_handler (globalhandler);

Return memory;

// this sets each currenthandler to 0Template new_handler newHandlersupport :: CurrentHandler;

With this template class, it is very simple to set the set_new_handler function: as long as X from NewHandlersupport inherit: // Note Inheritance from Mixin Base Class Template. (See // My Article on Counting Objects for Information ON Why // private inheritance might be preferable here.) Class X: Public NewHandlersupport {

... // as before, but no declarations for}; // set_new_handler or operator new

Still don't care about what it is doing after using X; the old code is still working. This is good! Those who you often don't want to pay often is often the most trustworthy.

Using SET_NEW_HANDLER is a convenient, simple method for processing insufficient memory. It is certainly much better than packing each New in the TRY module. Moreover, NewHandlersupport makes it easier to add a specific New-handler to any class. The inheritance of "mixed style" is inevitably introduced to inherit the topic. Before transferring to this topic, you must read the terms 43.

Before 1993, C has been asked to return 0 when the memory allocation fails, and now the Operator New throws std :: Bad_alloc exceptions. Many C programs are written in front of the compiler to support new specifications. The C Standards Committee does not want to give up the code that has followed the 0 specification, so they provide additional form Operator New (and Operator New [] - see Terms 8) to continue to provide the return 0 function. These forms are called "no throw" because they have not used a throw, but in the use of Nothrow objects in the entry point of New: Class Widget {...}; widget * pw1 = new widget; // Assign Failure throw std :: bad_alloc

IF (PW1 == 0) ... // This check must fail Widget * PW2 = new (notHrow) widget; // If the assignment fails to return 0

IF (PW2 == 0) ... // This check may succeed

Regardless of the "formal" (ie, throwing anomalies), New is still "no throw" form, is important, you must prepare for memory allocation failure. The easiest way is to use SET_NEW_HANDLER because it is useful for both forms.

转载请注明原文地址:https://www.9cbs.com/read-4476.html

New Post(0)