This would be very bad, but since operator delete
can be overloaded a sloppy programmer might have omitted the test for null pointers, thus requiring the client to check for it. This might be a reason for it here.
Another point, setting the pointer explicitly to null after deleting might actually make sense depending on the architecture of the code, precisely to prevent double-deletes. Since this only saves double-deletes on the same pointer though, it's highly questionable whether this is a good design. I wouldn't ever rely on it.
An explanation of overloaded operator delete
:
operator delete
is (despite its name) a function that may be overloaded like any other function. This function gets called internally for every call of operator delete
with matching arguments. The same is true for operator new
.
Overloading operator new
(and then also operator delete
) makes sense in some situations when you want to control precisely how memory is allocated. Doing this isn't even very hard, but a few precautions must be made to ensure correct behaviour. Scott Meyers describes this in great detail Effective C++.
For now, let's just say that we want to overload the global version of operator new
for debugging. Before we do this, one short notice about what happens in the following code:
klass* pobj = new klass;
// … use pobj.
delete pobj;
What actually happens here? Well the above can be roughly translated to the following code:
// 1st step: allocate memory
klass* pobj = static_cast<klass*>(operator new(sizeof(klass)));
// 2nd step: construct object in that memory, using placement new:
new (pobj) klass();
// … use pobj.
// 3rd step: call destructor on pobj:
pobj->~klass();
// 4th step: free memory
operator delete(pobj);
Notice step 2 where we call new
with a slightly odd syntax. This is a call to so-called placement new
which takes an address and constructs an object at that address. This operator can be overloaded as well. In this case, it just serves to call the constructor of the class klass
.
Now, without further ado here's the code for an overloaded version of the operators:
void* operator new(size_t size) {
// See Effective C++, Item 8 for an explanation.
if (size == 0)
size = 1;
cerr << "Allocating " << size << " bytes of memory:";
while (true) {
void* ret = malloc(size);
if (ret != 0) {
cerr << " @ " << ret << endl;
return ret;
}
// Retrieve and call new handler, if available.
new_handler handler = set_new_handler(0);
set_new_handler(handler);
if (handler == 0)
throw bad_alloc();
else
(*handler)();
}
}
void operator delete(void* p) {
cerr << "Freeing pointer @ " << p << "." << endl;
free(p);
}
This code just uses malloc
/free
internally, much as most default implementations. It also creates a debugging output. Consider the following code:
int main() {
int* pi = new int(42);
cout << *pi << endl;
delete pi;
}
It yielded the following output:
Allocating 4 bytes of memory: @ 0x100160
42
Freeing pointer @ 0x100160.
Now, this code does something fundamentally different than the standard implementation of operator delete
: It didn't test for null pointers! The compiler doesn't check this so the above code compiles but it may give nasty errors at run-time when you try to delete null pointers.
However, as I said before, this behaviour is actually unexpected and a library writer should take care to check for null pointers in the operator delete
. This version is much improved:
void operator delete(void* p) {
if (p == 0) return;
cerr << "Freeing pointer @ " << p << "." << endl;
free(p);
}
In conclusion, although a sloppy implementation of operator delete
may require explicit null checks in the client code, this is non-standard behaviour and should only be tolerated in legacy support (if at all).