Item 51: Follow the Conventions when writing new and delete
Item 51: Adhere to convention when writing new and delete.
Item 50 introduces how to customizenew
Anddelete
But there is no explanation of the Conventions you must follow. These conventions are not intuitive, so you need to remember them!
operator new
Resources need to be obtained in an infinite loop. If the resource cannot be obtained, an exception should be thrown when "new handler" is called and "new handler" does not exist;
operator new
Processingsize == 0
;
operator delete
Should be compatible with null pointers;
operator new/delete
Processing as a member functionsize > sizeof(Base)
(Because of inheritance ). External operator newItem 49 shows howoperator new
Reload is a member function of the class. Here we first look at how to implement an external (non-member function)operator new
:operator new
There should be a correct return value. When the memory is insufficient, you should call "new handler". The request can also be executed normally when the memory size is 0 to avoid hiding the global ("normal form ")new
.
- It is easy to return values. If the memory is sufficient, the requested memory address is returned. If the memory is insufficient, an empty address or throw is returned according to the rules described in Item 49.
bad_alloc
Exception.
- It is not easy to call "new handler" upon each failure and apply for memory again. An exception should be thrown only when "new handler" is null.
- A valid pointer should also be returned when the request size is zero. A space of zero size is allowed for programming.
Considering the above objective, a non-member function'soperator new
The general implementation is as follows:
Void * operator new (std: size_t size) throw (std: bad_alloc) {if (size = 0) size = 1; while (true) {// try to apply for void * p = malloc (size); // if the application is successful (p) return p; // The application fails, get new handler new_handler h = set_new_handler (0); set_new_handler (h); if (h) (* h) (); else throw bad_alloc ();}}
size == 0
The application size is1
It seems inappropriate, but it is very simple and can work properly. Besides, you will not often apply for a space of 0, right?
- Twice
set_new_handler
The global "new handler" is set to null before the call is returned. This is because the "new handler" cannot be directly obtained. The lock is required in the multi-threaded environment.
while(true)
This may be an endless loop. As mentioned in Item 49, "new handler" either releases more memory or installs a new "new handler ", if you implement a useless "new handler", this is an endless loop. Operator newHeavy Loadoperator new
A member function is usually used to optimize the dynamic memory management of a specific class, rather than for its subclass. BecauseBase::operator new()
Is based on the object sizesizeof(Base)
Memory Management optimization.
Of course, in some casesBase::operator new
This rule is not applicable to the entire class and its subclass.
Class Base {public: static void * operator new (std: size_t size) throw (std: bad_alloc) ;}; class Derived: public Base {...}; derived * p = new Derived; // The Base: operator new! is called!
Subclass inheritanceBase::operator new()
Later, because the current object is no longer a hypothetical size, this method is no longer suitable for managing the memory of the current object. You canBase::operator new
Determining Parameterssize
When big or smallsizeof(Base)
Globalnew
:
void *Base::operator new(std::size_t size) throw(std::bad_alloc){ if(size != sizeof(Base)) return ::operator new(size); ...}
The above code is not checkedsize == 0
! This is the magic of C ++. An independent object with a size of 0 will be inserted.char
(See Item 39 ). Sosizeof(Base)
It will never be 0, sosize == 0
To::operator new(size)
.
Here I will mentionoperator new[]
, It andoperator new
It has the same parameters and return values. Note that you should not assume that there are several objects and the size of each object. Therefore, do not operate on these non-existing objects. Because:
- You don't know what the object size is. As mentioned above, when Inheritance occurs
size
Not necessarily equalsizeof(Base)
.
size
The value of a real parameter may be greater than the sum of the values of these objects. As mentioned in Item 16, the array size may also need to be stored. External operator deleteComparednew
, Implementationdelete
The rules are much simpler. The only thing to note is that C ++ ensuresdelete
OneNULL
Always safe. You just need to respect this practice.
Similarly, first implement an external (non-member)delete
:
Void operator delete (void * rawMem) throw () {if (rawMem = 0) return; // release memory}
Operator delete MemberThe delete of member functions is also very simple, but note that if yournew
Othersize
Application, thendelete
You should also forward othersize
.
Class Base {public: static void * operator new (std: size_t size) throw (std: bad_alloc); static void operator delete (void * rawMem, std: size_t size) throw () ;}; void Base: operator delete (void * rawMem, std: size_t size) throw () {if (rawMem = 0) return; // check the NULL pointer if (size! = Sizeof (Base) {: operator delete (rawMem);} // release memory}
Note that the above check israwMem
Null,size
It is not empty.
Actuallysize
The value of the real parameter is derived by the caller type (if there is no virtual destructor ):
Base * p = new Derived; // assume that Base ::~ Base is not a virtual function delete p; // input 'size = sizeof (Base) 'of 'delete (void * rawMem, std: size_t size )'.
IfBase::~Base()
Declaredvirtual
size
Is correctsizeof(Derived)
. This is why Item 7 points out that the Destructor must be declared.virtual
.