E. Robert Tisdale said:
I object to your abuse of the English language.
The pointer p is *not* deleted.
I don't think Andrew said so; he just said: "after the first "delete p"". He
didn't say that the pointer was deleted.
The *object* to which pointer p points is deleted.
Pointer p is a valid pointer to an invalid object
(of type int in this case) because delete calls
the object's destructor to destroy the object.
The problem is that the second delete
is almost certainly a programming error -- a bug.
If you simply set the pointer p = NULL,
the bug will go undetected.
It is not necessarilly a bug. There is a reason why you can do a delete on a
null pointer.
You can't do anything about this for built-in types
but you can for User Defined Types (UDTs):
class X {
private:
int Valid;
// other data members
public:
X(void) Valid(0x55555555) { }
~X(void) {
if (0x55555555 == Valid)
Valid == 0xaaaaaaaa;
else
std::cerr << "Invalid object of type X!" << std::endl;
}
// other public functions and operators
};
Even though this technique may work on some platforms with some compilers
under certain circumstances, it is not *guaranteed* to work. Once an object
has been deleted, the memory it occupied may have been overwritten, unmapped
(resulting in a page fault) or occupied by another object (possibly of the
same class). There is also a chance the vtable pointer of the deleted object
is damaged; if the pointer is used to call any virtual member function
(including the destructor) on the deleted object very weird things may
happen - good for hours of debugging fun.
Personnally I prefer resetting the pointer to NULL after deleting the object
it is pointing to. That way it easy to detect if the pointer is still
pointing to a valid object, and on most platforms leads to very predictable
behaviour when that pointer is accidentally dereferenced. Resetting the
pointer does not conflict with using a member variable to see if the object
is still valid. Even tough that technique is not 100% reliable, it increases
the chance that code that shouldn't work doesn't work (which is usually a
good thing). Member functions (not just the destructor) can use the member
variable with the magic number as a precondition. This technique can be
especially useful to detect lifetime issues i.c.w. having multiple pointers
to the same object.