Effective C ++ 2e Item8

zhaozj2021-02-11  184

Terms 8. Write Operator New and Operator Delete to follow regular

When you rewrite Operator New (Terms 10 explains why sometimes to rewrite it), it is important to be the behavior provided by the function and the system default Operator New consistent. It is actually what is also: there is a correct return value; the available memory is not enough to call the error handler (see Terms 7); process the 0 byte memory request. In addition, it is necessary to avoid accidentally hidden standard forms, but this is the topic of clause 9.

The part of the return value is simple. If the memory allocation request is successful, return a pointer to the memory; if it fails, follow the provisions of the clause 7 throw a std :: Bad_alloc type exception.

But things are not so simple. Because the Operator New actually tries to assign memory at a time, it is necessary to call an error handler after each failure, and it is also desirable that the error handler can find ways to release the memory. The Operator New throws an exception if the pointer to the error handler is empty.

In addition, the C standard requires that the Operator New is also returned to a legitimate pointer even when the 0 byte memory is requested. (In fact, this sounds are strange, it is easy to bring easy to C language elsewhere)

In this way, the pseudo code of the Operator New in the form of a non-class member looks like this: void * operator new (size_t size) // Operator New may have other parameters {

If (size == 0) {// Treat 0 byte request, size = 1; // Treat it as a one byte request} while (1) {assign SIZE byte memory;

IF (allocated successfully) Return (pointing to memory);

// Distribution is unsuccessful, find the current error handage function new_handler globalhandler = set_new_handler (0); set_new_handler (GlobalHandler);

IF (GLOBALHANDAL) (* GlobalHandler) (); else throw std :: bad_alloc ();}}

The skill of processing the zero-byte request is to process it as a request byte. This looks very blame, but simple, legal, effective. Moreover, how long will you encounter a zero-byte request?

You will be strange why the error processing function is set to 0 after the pseudo code above. This is because there is no way to get a pointer to the error handling function, so you must find it by calling set_new_handler. The way is very stupid but is also effective.

Terms 7 mentioned that the Operator New includes an infinite loop. The above code clearly shows this point --While (1) will result in an infinite loop. The only way to jump out the loop is that the memory allocation is successful or the error handler completed one of the events described in Terms 7: Get more available memory; installed a new new-handler (error handler); unload New-handler; throw a std :: Bad_alloc or its derived type of exception; or return fails. Now I understand why new-handler must do one of these work. If not, the loop inside Operator New will not end.

Many people don't realize that Operator New often inherits by subclasses. This can lead to certain complexity. In the pseudo code above, the function will be assigned the SIZE byte memory (unless size is 0). SIZE is important because it is the parameter passed to the function. However, most of the Operator New (including Terms 10) written for classes is only designed for a particular class, not for all classes, nor for all of its subclass. This means that for a class X's Operator New, the behavior inside the function involves the overall size of the object, is accurate SIZEOF (X): Will not be large. However, due to the existence of inheritance, the Operator New in the base class may be called to allocate memory for a sub-class object: class base {public: static void * operator new; ...}; class derived: public Base / / Derived class has no statement Operator new {...}; //

Derived * p = new derived; // Call Base :: Operator New

If the Base class's Operator New does not want to pay for this situation - this situation is not possible - the easiest way is to transfer this "wrong" memory allocation request to standard operator new Processing, like this: void * base :: operator new (size! = Sizeof (base)) // If the number "error", the standard operator new return :: Operator new (size); / / Go to handle this request //

... // Otherwise handle this request}

"Stop!" I heard you calling, "You forgot to check a situation although it is unreasonable but there may be anything - Size may be zero!" Yes, I didn't check, but please call next time. Don't be such a literary. :) But actually check or do it, but integrated into the Size! = SizeOf (Base) statement. The C standard is very weird, one of which is to specify that the independent (free "class is a non-zero value. So SizeOf (Base) can never be zero (even if the base class is not a member), if size is zero, the request will go to :: Operator New, which is processed in a reasonable way. (Interestingly, if Base is not an independent class, sizeof (base) may be zero, detailed see "My Article on Counting Objects").

If you want to control the memory allocation based on the class-based array, you must implement the array of Operator New. ). When writing Operator New [], you should remember that you face "original" memory, you cannot do anything about objects that don't exist in arrays. In fact, you don't even know how many objects in the array, because I don't know how big each object is. The Operator New [] of the base class is used to allocate memory as an array of sub-objects in the manner, while sub-class objects are often larger than base categories. So, I can't think of that the size of each object in Base :: Operator New [] is sizeof (base), that is, the number of objects in the array is not necessarily (request byte) / sizeof (base). See the Terms M8 for details on Operator New []. Override Operator New (and Operator New []) All regular regulations are these. For Operator Delete (and its partner Operator Delete []), the situation is simpler. What you have to remember is that C guarantees that the empty pointer is always safe, so you have to fully apply this guarantee. Here is the pseudode code of Operator delete in the form of non-class members: void Operator delete (rawmemory == 0) Return; file: // If the pointer is empty, return //

Release RawMemory points to the memory;

Return;}

The class member version of this function is also simple, but it must also check the size of the object being deleted. Assuming the class's Operator New forwards the "error" size assignment request to :: Operator New, then the "Error" size must be transferred to :: Operator Delete:

Class base {//, the same as front, just declare public: // Operator delete static void * operator new; static void operator delete (void * rawMemory, size_t size); ...};

Void Base :: operator delete (void * rawMemory, size_t size) {if (rawMemory == 0) Return; // Check the empty pointer

IF (size! = sizeof (base)) {// If the size "error", :: operator delete (RawMemory); // Let the standard operator to process the request Return;}

Release pointing to RawMemory;

Return;}

It can be seen that the rules about Operator New and Operator delete (and their array form) are not so troublesome, and it is important to follow it. As long as the memory allocation supports the New-Handler function and correctly handles the zero memory request; if the memory release program handles the empty pointer, then there is nothing else to do. As for adding inheritance support in the function of the class member version, it will soon be completed.

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

New Post(0)