Gotw # 10 Memory Management - Part II
Author: Herb Sutter
Translation: Kingofark
[Declaration]: This article takes the Guru of The Week column on www.gotw.ca website, and its copyright belongs to the original person. Translator Kingofark translated this article without the consent of the original person. This translation is only for self-study and reference, please read this article, do not reprint, spread this translation; people download this translation content, please delete its backup immediately after reading. Translator Kingofark is not responsible for people who violate the above two principles. This declaration.
Revision 1.0
GURU of The Week Terms 10: Memory Management (Part 2)
Difficulty: 6/10
(Are you considering your own specific memory management plan for a class? Even if you want to simply replace C global operator new and delete? Try the question below!)
[problem]
The following code is taken from a program, and this program has some types of memory management schemes. Please identify the error related to memory as much as possible and answer the additional questions in your comments.
/ / Why is the DELETE of BI still has a second parameter?
/ / Why did the DELETE does not have a second parameter?
//
Class b {
PUBLIC:
Virtual ~ b ();
Void Operator Delete (Void *, SIZE_T) throw ();
Void Operator delete [] (void *, size_t) throw ();
Void f (void *, size_t) throw ();
}
Class D: public b {
PUBLIC:
Void Operator Delete (Void *) throw ();
Void Operator delete [] (void *) throw ();
}
Void f ()
{
/ / In each of the statements below, which one delete is called? why?
// What is the parameter when calling?
//
D * pd1 = new d;
DELETE PD1;
B * PB1 = New D;
DELETE PB1;
D * pd2 = new d [10];
delete [] PD2;
B * PB2 = New d [10];
Delete [] PB2;
// Is the following two assignment statements legally?
//
B B;
Typedef void (b :: * pmf) (void *, size_t);
PMF P1 = & b :: f;
PMF P2 = & b :: Operator delete;
}
Class x {
PUBLIC:
Void * Operator New (size_t s, int)
Throw (BAD_ALLOC) {
Return :: Operator New (s);
}
}
Class sharedmemory {
PUBLIC:
Static void * allocate (size_t s) {
Return OsSpecificsharedMallocation (s);
}
Static void deallocate (Void * P, INT i) {
OsSpecificsharedMemdeallocation (p, i);
}
}
Class y {
PUBLIC:
Void * Operator new (size_t s,
SharedMemory & M) Throw (Bad_alloc) {
Return M.allocate (s);
}
Void Operator Delete (void * p,
SharedMemory & M,
INT i) throw () {
M.deallocate (P, I);
}
}
Void Operator Delete (Void * P) throw () {
SharedMemory :: deallocate (P);
}
Void Operator Delete (void * p,
Std :: nothrow_t &) throw () {
SharedMemory :: deallocate (P);
}
[answer]
/ / Why is the DELETE of BI still has a second parameter?
/ / Why did the DELETE does not have a second parameter?
//
Class b {
PUBLIC:
Virtual ~ b ();
Void Operator Delete (Void *, SIZE_T) throw ();
Void Operator delete [] (void *, size_t) throw ();
Void f (void *, size_t) throw ();
}
Class D: public b {
PUBLIC:
Void Operator Delete (Void *) throw ();
Void Operator delete [] (void *) throw ();
}
Additional answer: This is for personal preferences. Both are normal recycling (translation: the author used the word "deallocation", and the monks were translated into "recycling") functions, not Placement deletes (Mode note: About Placement Delete and Placement New, you can Read the segment of the book in the book in STANLEY LIPPMAN's "Inside THE C Object Model".
In addition, these two classes offer delete and delete [], but there is no corresponding New and New []. This is very dangerous. You can think of what happens when the lower derived class provides its own new and new []!
Void f ()
{
/ / In each of the statements below, which one delete is called? why?
// What is the parameter when calling?
//
D * pd1 = new d;
DELETE PD1;
Here is called D :: Operator Delete (void *).
B * PB1 = New D;
DELETE PB1;
Call D :: Operator Delete (void *) here. Since B's destructor is declared as Virtual, D's destructor is based on normal calls, but this also means that even if B :: Operator delete () does not declare into Virtual, D :: Operator delete () will also be called.
D * pd2 = new d [10];
delete [] PD2;
Here D :: Operator delete [] (void *) is called.
B * PB2 = New d [10];
Delete [] PB2;
The behavior here is unpredictable. C language requirements, the static type of pointer to delete must be the same as its dynamic type. For further discussions about this problem, see SCOTT MEYERS in "Effective C " or "More Effective C " section of "Never Treat Arrays Polymorph" (translation: Here, it should be "More Effective C " clause 3: Never deal with arrays in a polymorphic manner). // Is the following two assignment statements legally?
//
B B;
Typedef void (b :: * pmf) (void *, size_t);
PMF P1 = & b :: f;
PMF P2 = & b :: Operator delete;
}
The first assignment statement is no problem, but the second is illegal, because "Void Operator Delete (void *, size_t) throw ()" is not a member function of B, even if it is written on it, it looks very Like it is. There is a small trick to figure out, namely New and Delete are always static, even if they are not explicitly declared as static. Always declare that STITIC is a good habit, which allows all the programmers who read your code to understand this.
Class x {
PUBLIC:
Void * Operator New (size_t s, int)
Throw (BAD_ALLOC) {
Return :: Operator New (s);
}
}
This produces memory leaks because there is no corresponding Placement delete existence. The following code is also the same:
Class sharedmemory {
PUBLIC:
Static void * allocate (size_t s) {
Return OsSpecificsharedMallocation (s);
}
Static void deallocate (Void * P, INT i) {
OsSpecificsharedMemdeallocation (p, i);
}
}
Class y {
PUBLIC:
Void * Operator new (size_t s,
SharedMemory & M) Throw (Bad_alloc) {
Return M.allocate (s);
}
There is also a memory leak here because there is no corresponding delete. If an exception is thrown during the construction of the object with this function, the memory will not be released normally. E.g:
Sharedmemory shared;
...
NEW (Shared) T; // IF T :: t () throws, memory is leaked
Here, memory is still unable to be deleted because there is no normal Operator Delete in the class. This means that the base class or derived Operator delete (or that globally delete) will try to process this recycling operation (this will certainly fail unless you replace all similar delete in the surrounding environment ". A cumbersome and terrible thing).
Void Operator Delete (void * p,
SharedMemory & M,
INT i) throw () {
M.deallocate (P, I);
}
}
This delete here is useless because it will never be called.
Void Operator Delete (void * p) throw () {sharedmemory :: deallocate (p);
}
This is a serious mistake because it will delete the memory allocated by the default :: Operator New, not the memory assigned by SharedMemory :: allocate (). You can only hope to take a quick core dump. It's really a ghost!
Void Operator Delete (void * p,
Std :: nothrow_t &) throw () {
SharedMemory :: deallocate (P);
}
It is also the same here, just a more subtle. The delete here will only be called when "Nothrow) fails, because the constructor will terminate with an exception, and attempted to recycle those not by sharedmemory :: allocate () assignment Memory. This is really evil!
Ok, if you have answered all the questions, then you must go to the Guangming Avenue of the Memory Management Mechanism.