Reading Notes Objective c ++ Item 52 If you implement placement new, you also need to implement placement delete and inclutiveplacement

Source: Internet
Author: User

Reading Notes Objective c ++ Item 52 If you implement placement new, you also need to implement placement delete and inclutiveplacement
1. What will happen if an exception is thrown by calling operator new of the normal version?

Placement new and placement delete are not the most common beasts in the C ++ Zoo, so you don't have to worry about getting familiar with them. When you implement a new expression like the following, recall Item 16 and Item 17:

1 Widget *pw = new Widget;

 

Two functions will be called: one is to call operator new to allocate memory, and the other is the default constructor of the Widget.

Assume that the first call is successful, but an exception is thrown when the second function is called. In this case, the memory allocated in step 1 must be rolled back. Otherwise, memory leakage occurs. Client code cannot release memory, because if the Widget constructor throws an exception, pw will never assign a value. The client cannot obtain a pointer pointing to the memory to be released. The responsibility for performing rollback in step 1 falls on the C ++ runtime system.

The runtime system is very happy to call the operator delete corresponding to the operator new version called in step 1, but it only knows which operator delete (there may be many) it can be done only when a function is called. If the new and delete versions you are processing have a normal signature, this is not a problem, because the normal operator new,

1 void* operator new(std::size_t) throw(std::bad_alloc);

 

Corresponding to the normal operator delete:

1 void operator delete(void *rawMemory) throw();     // normal signature2 // at global scope3 4 void operator delete(void *rawMemory, std::size_t size) throw();   // typical normal signature at class  scope        

 

2. What will happen if an exception is thrown when custom operator new is called? 2.1 A problematic example

If you are using a common form of new and delete, the system can find the delete of the new version to perform the rollback operation. However, if you begin to declare the new version of a non-common version, that is, to generate a version with parameters, the problem arises: "Which delete is the version of the new version.

 

For example, if you implement operator new of a specific version of the class, it needs to specify an ostream to record the memory allocation information, you have also implemented a normal operator delete for a specific version of the class:

 1 class Widget { 2 public: 3 ... 4  5 static void* operator new(std::size_t size, 6  7                                                          // non-normal 8 std::ostream& logStream)                    // form of new 9 throw(std::bad_alloc);10 static void operator delete(void *pMemory, // normal class11 std::size_t size) throw(); // specific form12 // of delete13 ...14 };

 

2.2 description of related terms

This design is problematic, but before we discuss the cause, we need to describe related terms.

When an operator new function carries additional parameters (except the size_t parameter that must be included), we know that this is the new placement version. The above operator new is such a placement version. A particularly useful placement new is a pointer parameter that specifies where the object should be built. It will look like the following:

1 void* operator new(std::size_t, void *pMemory) throw(); // “placement2 // new”

 

The new version is part of the C ++ standard library. You can access it as long as you # inlucde <new>. It is also used to create objects in the unused space of a vector. It is also the earliest placement new. In fact, this is also the basis for the naming of this function: new at a specific position. This means that "placement new" is overloaded. In most cases, when people talk about placement new, they discuss this specific function, that is, operator new with an additional void * parameter. In rare cases, they discuss any version of operator new with additional parameters. The context of the Program often removes this ambiguity, but understanding the general term "placement new" means that any new version with additional parameters is very important, because "placement delete" (we will encounter it later) is derived from it directly.

2.3 How to solve the problem

Now let's go back to the declaration of the Widget class. I mentioned earlier that this design is problematic. The difficulty lies in the subtle Memory leakage of this class. Consider the following Customer Code. When creating a Widget dynamically, it records the memory allocation information to cerr:

1 Widget *pw = new (std::cerr) Widget; // call operator new, passing cerr as2 // the ostream; this leaks memory3 // if the Widget constructor throws

 

When the memory allocation is successful but the Widget constructor throws an exception, the system has the responsibility to roll back the allocation work executed by operator new during the runtime. However, the runtime system cannot really understand how the called operator new Version works, so it cannot perform rollback on its own. On the contrary,The system will findOperator delete, It andOperator newAdditional parameters with the same quantity and typeIf yes, this is the version to be called. In the above example, operator new has an additional parameter ostream &, so the corresponding operator delete is:

1 void operator delete(void*, std::ostream&) throw();

 

Compared with the new placement version, the operator delete version with additional parameters is called placement delete. In this case, the Widget does not declare the placement version of operator delete, so the system does not know how to roll back the placement new operation during runtime. Therefore, it does not do anything. In this example, if the Widget constructor throws an exception, no operator delete will be called!

The rule is simple:If an additional parameter is includedOperator newThere is no matching additional parameterOperator deleteVersion, ifNewThe memory allocation operation needs to be rolled back, so there is noOperator deleteWill be called. To eliminate the memory leakage of the above Code, the Widget needs to declare a placement delete corresponding to the placement new version of the log record:

 1 class Widget { 2 public: 3 ...             4  5 static void* operator new(std::size_t size, std::ostream& logStream) 6 throw(std::bad_alloc); 7 static void operator delete(void *pMemory) throw(); 8 static void operator delete(void *pMemory, std::ostream& logStream) 9 throw();10 ...11 };

 

With this change, if an exception is thrown from the Widget constructor in the following statement:

1 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.

 

3. What will happen when delete is called?

However, if no exception is thrown, we will delete it in the client code:

1 delete pw; // invokes the normal2 // operator delete

 

As described in the comment, this will call the normal operator delete instead of the placement version. Placement delete is triggered only when an exception is thrown when a matched placement new is called in the constructor. Using delete For a pointer (just like the pw above) will never call the placement version of delete.

This means that in order to preemptively launch the memory leakage caused by the new placement version, you must also provide the normal version of operator delete (called when no exception is thrown during construction ), and the placement version with the same additional parameters as placement new (called when an exception is thrown ). By doing this, you will never have to fall asleep on the tossing and turning sides of the subtle issues of Memory leakage.

4. Note the name hiding problem.

By the way, because the member function name hides the same name in the peripheral scope (see Item 33 ), you need to be careful not to hide other versions (including common versions) required by the new version of the class ). For example, if you have a base class that only declares the placement version of operator new, the customer will find that they cannot use the normal version of new:

 1 class Base { 2 public: 3 ... 4 static void* operator new(std::size_t size, // this new hides 5 std::ostream& logStream) // the normal 6 throw(std::bad_alloc); // global forms 7 ... 8 }; 9 10 Base *pb = new Base;                  // error! the normal form of11 // operator new is hidden12 13 Base *pb = new (std::cerr) Base; // fine, calls Base’s14 // placement new

 

Similarly, operator new in the derived class hides the global and inherited versions of operator new at the same time:

 1 class Derived: public Base {         // inherits from Base above 2  3  4 public: 5 ... 6 static void* operator new(std::size_t size) // redeclares the normal 7 throw(std::bad_alloc); // form of new 8 ... 9 };10 Derived *pd = new (std::clog) Derived; // error! Base’s placement11 // new is hidden12 Derived *pd = new Derived; // fine, calls Derived’s13 // operator new

 

This type of name hiding is discussed in detail in Item 33. However, to implement the memory allocation function, you need to remember that by default, C ++ provides the following operator new versions globally:

1 void* operator new(std::size_t) throw(std::bad_alloc);          // normal new2 3 void* operator new(std::size_t, void*) throw();    // placement new4 5 void* operator new(std::size_t,                             // nothrow new —6 const std::nothrow_t&) throw(); // see Item 49

 

If you declare any operator new in the class, you will hide these standard versions. Unless your intention is to prevent customers from using these versions, ensure that these standard versions are available to customers except for any custom operator new versions you have created. For each operator new you provide, ensure that the corresponding operator delete is provided at the same time. If you want these functions to behave like normal functions, you can call the global version for a specific version of your class.

A simple method to achieve this is to create a base class containing all new and delete versions:

 1 class StandardNewDeleteForms { 2 public: 3 // normal new/delete 4 static void* operator new(std::size_t size) throw(std::bad_alloc) 5 { return ::operator new(size); } 6 static void operator delete(void *pMemory) throw() 7 { ::operator delete(pMemory); } 8  9 // placement new/delete10 static void* operator new(std::size_t size, void *ptr) throw()11 { return ::operator new(size, ptr); }12 static void operator delete(void *pMemory, void *ptr) throw()13 { return ::operator delete(pMemory, ptr); }14 // nothrow new/delete15 static void* operator new(std::size_t size, const std::nothrow_t& nt) throw()16 { return ::operator new(size, nt); }17 static void operator delete(void *pMemory, const std::nothrow_t&) throw()18 { ::operator delete(pMemory); }19 };

 

If you want to add the standard version based on the custom version, you only need to inherit the base class and then use the using declaration to get the standard version (Item 33:

 1 class Widget: public StandardNewDeleteForms {       // inherit std forms 2  3 public:                                                                4  5  6 using StandardNewDeleteForms::operator new; // make those 7  8 using StandardNewDeleteForms::operator delete;    // forms visible 9 10 static void* operator new(std::size_t size,        // add a custom11 12 13 std::ostream& logStream) // placement new14 throw(std::bad_alloc);15 static void operator delete(void *pMemory, // add the corres16 std::ostream& logStream) // ponding place17 throw(); // ment delete18 ...19 };

 

5. Summary

 

  • When you implement the placement version of operator new, make sure that the corresponding operator delete placement version is implemented. If you do not implement it, some programs may experience subtle and intermittent memory leaks.
  • When you declare the placement versions of new and delete, make sure that you do not inadvertently hide the normal versions of these functions.

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.