Effective C ++, 3rd edition, Item 52: If placement new is written, you need to write placement Delete.

Source: Internet
Author: User

Item 52: If placement new is written, you need to write placement Delete.

By Scott Meyers

Translator: fatalerror99 (itepub's nirvana)

Release: http://blog.csdn.net/fatalerror99/

In the C ++ Zoo, placement new and placement Delete are not the most common beasts, so don't worry if you are unfamiliar with them. As an alternative, think about items 16 and 17. When you write a new expression like this,

Widget * PW = new widget;

Two functions are called: Operator New for memory allocation, and default constructor for widgets (default constructor ).

Assume that the first call is successful, and the second call throws an exception ). In this case, the memory allocation completed in step 1 must be revoked. Otherwise, it is a memory leak. The Customer Code cannot recycle the memory, because if the constructor (constructor) of the widget throws an exception, PW is not assigned a value at all. For the customer, the pointer to the memory that should be recycled cannot be obtained. Therefore, the role of revoking Step 1 is bound to the C ++ runtime system (C ++ runtime system.

The runtime system appropriately calls the operator Delete corresponding to the operator new version called in step 1, but it can do this only when it knows which operator Delete-there may be many-most appropriate. If you are playing with new and delete versions with regular signatures (recognition features), this is not a problem because the regular operator new,

Void * operator new (STD: size_t) Throw (STD: bad_alloc );

Corresponding to the regular operator delete:

Void operator Delete (void * rawmemory) Throw (); // normal Signature
// At global scope

Void operator Delete (void * rawmemory, // typical normal
STD: size_t size) Throw (); // signature at class
// Scope

When you only use the regular form of new and delete, the runtime system (runtime system) can find out how to cancel the delete operation of new. However, when you begin to declare the unconventional form of operator New-with the form of additional parameters, which-delete-goes-with-this-New (which delete is paired with this new).

For example, if you have compiled a class-specific (Class-specific) operator new, it requires an ostream Specification Description used to record the allocation information, you have compiled a general class-specific (Class-specific) operator delete:

Class widget {
Public:
...
Static void * operator new (STD: size_t size, // non-normal
STD: ostream & logstream) // form of new
Throw (STD: bad_alloc );

Static void operator Delete (void * pmemory // normal class-
STD: size_t size) Throw (); // specific form
// Of Delete
...
};

This design is problematic, but before we explore why, we need to provide a brief glossary.

When an operator new function holds additional parameters (except the required size_t parameter), this function is called the newPlacementVersion. The operator new is such a placement version. There is a particularly useful placement new, which holds a pointer that specifies the position where an object is constructed. The operator new is as follows:

Void * operator new (STD: size_t,Void * pmemory) Throw (); // "placement
// New"

This version of New is part of the C ++ standard library. You can access it as long as # include <New>. It should be noted that this new is used inside the vector to create objects in the unused space of the vector. It is also the initial placement new. In fact, this is the type of function calledPlacement new. This means that the term "placement new" is given more meaning. In most cases, when people talk about placement new, they talk about this specific function, and operator new with an extra parameter of the void * type. In rare cases, they talk about any version of operator new with additional parameters. According to the context, you can usually understand any ambiguity. The important thing is to understand the general term "placement new", which means any version of New with additional parameters, because the phrase "placement Delete" (we will see it later) originated directly from it.

Let's first return to the declaration (Declaration) of the widget class, which is the one that is designed to be a problem. The trouble is that this class will cause a subtle memory leaks (Memory leakage ). Consider the following Customer Code. When a widget is dynamically created, it will allocate information in the cerr record:

Widget * PW = new (STD: cerr) widget; // call operator new, passing cerr
// The ostream;This leaks memory
//If the widget constructor throws

Once again, if the memory allocation is successful and the widget constructor throws an exception, the runtime system has the responsibility to cancel the allocation executed by operator new. However, the runtime system does not really understand how the called operator New Version works, so it cannot cancel the allocation by itself. The runtime system looks for an operator Delete version that holds the same number and type of additional parameters as operator new, and if it finds it, it calls it. In the current situation, operator new holds an ostream & type additional parameter, so the corresponding operator Delete should have such signature (Recognition Feature ):

Void operator Delete (void *,STD: ostream &) Throw ();

Similar to the placement version of new, the operator Delete version with additional parameters is calledPlacement deletes. In the current situation, the widget does not declare the placement version of operator Delete. Therefore, the runtime system does not know how to cancel the call of placement new. As a result, it does nothing. In this example, if the widget Constructor (constructor) throws an exception, noOperator DeleteCan be called!

The rule is simple: If an operator new with an additional parameter does not match the operator Delete with the same additional parameter, when a memory allocation generated by new needs to be revoked, no operator Delete can be called. To eliminate the memory leak (Memory Leak) in the previous code, the widget needs to declare a placement delete that corresponds to logging placement New:

Class widget {
Public:
...
Static void * operator new (STD: size_t size, STD: ostream & logstream)
Throw (STD: bad_alloc );
Static void operator Delete (void * pmemory) Throw ();

Static void operator Delete (void * pmemory, STD: ostream & logstream)
Throw ();
...
};

After this change, if an exception (exception) is thrown from the widget Constructor (constructor) of the following statement ),

Widget * PW = new (STD: cerr) widget; // as before, but no leak this time

The corresponding placement Delete is automatically called, which ensures that the widget has no memory leakage.

However, consider what will happen in the following situations. If no exception (exception) is thrown (this is the common case) and our Customer Code contains another delete:

Delete PW; // invokes the normal
// Operator Delete

As mentioned in the comment, the regular operator Delete is called instead of the placement version. Placement Delete is called only when an exception (exception) occurs when a constructor (constructor) associated with placement new is called. Applying Delete to a pointer (such as the PW above) will never cause a call to a delete placement version. Absolutely not.

This means that to prevent all memory leaks related to the new placement version (Memory leakage), you must provide both regular operator Delete (no exception thrown during the construction process (exception) and provide a placement version that holds the same extra arguments (additional parameters) as operator new (used in the opposite case ). In this way, you will no longer be unable to sleep due to the subtle memory leaks. Well, at least it won't be because of these subtle memory leaks ).

By the way, because the member function (member function) name will overwrite the peripheral function with the same name (see item 33), you need to be careful not to use Class-specific (Class-specific) the news of overwrite other news that your customers want to see (including its regular version ). For example, if you have a base class (base class) that only declares a placement version of operator new, the customer will find that the conventional form of new is unavailable to them:

Class base {
Public:
...

Static void * operator new (STD: size_t size, // This new hides
STD: ostream & logstream) // The normal
Throw (STD: bad_alloc); // global forms
...
};
Base * pb = new base; // error! The normal form
// Operator new is hidden

Base * pb = new (STD: cerr) base; // fine, callbase's
// Placement new

Similarly, operator news in the derived classes (derived class) overwrites the operator news global and the inherited version operator New:

Class derived: public base {// inherits from base above
Public:
...

Static void * operator new (STD: size_t size) // redeclares the normal
Throw (STD: bad_alloc); // form of new
...
};
Derived * Pd = new (STD: clog) derived; // error! Base's placement
// New is hidden

Derived * Pd = new derived; // fine, callderived's
// Operator new

Item 33 discusses the details to be considered for such name overwriting. If you plan to write a memory allocation function, remember that by default, c ++ provides operator new in the following form globally:

Void * operator new (STD: size_t) Throw (STD: bad_alloc); // normal New

Void * operator new (STD: size_t, void *) Throw (); // placement new

Void * operator new (STD: size_t, // nothrow new-
Const STD: nothrow_t &) Throw (); // see item 49

If you declare any operator news in a class, all these standard forms will be overwritten. Unless you intentionally prevent class customers from using these forms, make sure they are usable in addition to any custom new forms you create. Of course, make sure that operator Delete is provided for each operator new that you make available. If you want these functions to behave normally, you only need to call the global version for your class-specific (Class-specific) version.

A simple method to achieve this is to create a base class (base class) containing all the conventional forms of new and delete ):

Class standardnewdeleteforms {
Public:
// Normal New/delete
Static void * operator new (STD: size_t size) Throw (STD: bad_alloc)
{Return: Operator new (size );}
Static void operator Delete (void * pmemory) Throw ()
{: Operator Delete (pmemory );}

// Placement new/delete
Static void * operator new (STD: size_t size, void * PTR) Throw ()
{Return: Operator new (size, PTR );}
Static void operator Delete (void * pmemory, void * PTR) Throw ()
{Return: Operator Delete (pmemory, PTR );}

// Nothrow new/delete
Static void * operator new (STD: size_t size, const STD: nothrow_t & NT) Throw ()
{Return: Operator new (size, NT );}
Static void operator Delete (void * pmemory, const STD: nothrow_t &) Throw ()
{: Operator Delete (pmemory );}
};

Customers who want to add custom forms beyond the standard form can use the Inheritance (inheritance) and using declarations (use declarations) (see item 33) to get the standard form:

Class Widget: Public standardnewdeleteforms {// inherit STD forms
Public:
Using standardnewdeleteforms: Operator new; // make those
Using standardnewdeleteforms: Operator Delete; // forms visible

Static void * operator new (STD: size_t size, // Add a custom
STD: ostream & logstream) // placement new
Throw (STD: bad_alloc );

Static void operator Delete (void * pmemory, // Add the corres-
STD: ostream & logstream) // ponding place-
Throw (); // ment Delete
...
};

Things to remember

  • When compiling a placement version of operator new, make sure that the corresponding placement version of operator Delete is also written. Otherwise, your program may have a subtle, intermittent memory leaks (Memory leakage ).
  • When you declare the placement versions of new and delete, make sure that the regular versions of these functions are not overwritten.

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.