[comp.lang.c++] Curious about operator delete setting ptrs to zero...

warsaw@nlm.nih.gov (Barry A. Warsaw) (01/08/91)

Having been bitten lately with multiple destruction of freestore
allocated objects, I'm curious as to why operator delete does not, by
default, set the pointer whose object is being deleted to zero? I'm
sure there must be some good reasons why this is not done by default
in C++, and I can perhaps(?) come up with some scenarios where this
might be a Bad Thing, but in general why can't delete perform this
very useful function, and allow overloading of delete to bypass when
this would cause problems due to a particular class's implementation.
Aside from the reason that the language definition would have to
change to facilitate this (would it? see below).

Seems to me it would be a useful thing since deleting a null pointer
should be harmless but deleting an already freed block can have
disasterous effects.  Just to quickly illustrate with a totally
contrived example:

#include <stream.h>
class Foo {
public:
	Foo( int val ) { data = val; }
	int value() { return( data ); }
private:
	int data;
};


main() {
	Foo* foo = new Foo( 10 );
	cout << "foo = " << foo << ", foo's value is " << foo->value() << endl;
	delete foo;
	cout << "foo = " << foo << endl;
	// foo = 0; // necessary since delete doesn't set foo=0;
	delete foo; // if delete set foo=0, this would be harmless;
};

So enlighten me as to why the current functionality. Also, can you
provide a hint as to how one might go about overloading operator
delete or writing a destructor to give a class the ability to set the
pointer to zero?  As I see it, it would currently be difficult since
`this' doesn't get passed to the destructor by reference and operator
delete's first argument is a void*.  Seems to me it would have to be a
void*& to allow setting to zero.  Comments?

-Barry

steve@taumet.com (Stephen Clamage) (01/09/91)

warsaw@nlm.nih.gov (Barry A. Warsaw) writes:

>Having been bitten lately with multiple destruction of freestore
>allocated objects, I'm curious as to why operator delete does not, by
>default, set the pointer whose object is being deleted to zero?

Operator delete setting the pointer to zero is allowed but not
required.  (E&S says the value of the pointer is undefined.)
Setting it to zero solves only part of the problem, however, since
there may be other pointers in the program pointing to the now-deleted
object.

It is possible to make run-time reasonableness checks on pointers to
determine whether they point to space which has been since freed (and
possibly re-allocated).  It is expensive (at run-time) to do so, however.
This all comes under the heading of "quality of implementation".  There
is a place in the market for a spectrum of such checking by compilers.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

philip@pescadero.Stanford.EDU (Philip Machanick) (01/09/91)

In article <546@taumet.com>, steve@taumet.com (Stephen Clamage) writes:
|> warsaw@nlm.nih.gov (Barry A. Warsaw) writes:
|> 
|> >Having been bitten lately with multiple destruction of freestore
|> >allocated objects, I'm curious as to why operator delete does not, by
|> >default, set the pointer whose object is being deleted to zero?
|> 
|> Operator delete setting the pointer to zero is allowed but not
|> required.  (E&S says the value of the pointer is undefined.)
|> Setting it to zero solves only part of the problem, however, since
|> there may be other pointers in the program pointing to the now-deleted
|> object.
|> 
[...]
How is it possible for operator delete to set the value of the pointer,
since it is meant to be passed as void*? (You can't pass it as a reference
to void*.) Am I missing something?
-- 
Philip Machanick
philip@pescadero.stanford.edu

martino@microsoft.UUCP (Martin O'RIORDAN) (01/09/91)

The primary reason for not setting the pointer to NULL has to do with the
age old problem of aliases in C.  This has of course been inheritted by C++.
Consider the following code fragment :-

	void foo ()
	{
	    char * p, * q;

	    p = new char[ 25 ];
	    q = p;	// The alias

	    delete p;	// sets 'p' to NULL !!
	    delete q;	// But what about the alias ?
	}

Okay, so you can say that a clever compiler could do enough analyses to
determine that 'q' is a statically determinable alias for 'p'.  However,
typical alias problems are not so simple.  Pointers and heap are primarily
used to control object lifetimes in ways separate to the static/lexical
control flow of a program.  Pointers are often passed beyond the function
that allocated the object.  Consider :-

	extern void bar ( char * );

	void foo ()
	{
	    char * p;

	    p = new char[ 25 ];

	    bar ( p );	// The compiler doesn't know what 'bar' does with the value

	    delete p;	// What if 'bar' already deleted the space ?
	}

This case, despite being simple in form is impossible for a compiler based on
separate compilation technology to analyse for aliasing.  To achieve this kind
of thing, pointers would need to be handles which referred to the object only
through an indirect table, as in PASCAL.  The improved security for deletion
would be offset by the considerable increase in access cost for the object.

To add the facility for 'delete' to set the pointer provided to NULL, would
in effect impose a mechanism such as this on the language.  It is not a small
scale change.

	Martin O'Riordan

warsaw@nlm.nih.gov (Barry A. Warsaw) (01/10/91)

>>>>> "Martin" == Martin O'RIORDAN <martino@microsoft.UUCP> writes:

	Martin> The primary reason for not setting the pointer to NULL has
	Martin> to do with the age old problem of aliases in C.  This has
	Martin> of course been inheritted by C++.  Consider the following
	Martin> code fragment :-

[...fragment deleted...]

Except that with C++ we have a safer (and presumably, therefore,
better) way of doing true aliasing. Instead of:

	char *p, *q;

would it not be better to use:

	char *p;
	char *&q;

Then if p is ever set to zero, q would also become zero, regardless of
where in the program this occurred. Now, if you're really using two
distinct pointers with different positions into the same array, then
yes, you must use char *p and *q.  In this case, regardless of what
operator delete does, you the programmer are going to have to be very
careful in how you deallocate p and q.

My point is that no matter what operator delete does to the pointer
value, you are going to have to take the same precautions when using
*p = *q, and only a debugging environment like Saber-C++ is going to
help you catch multiple deallocation bugs (hopefully, I haven't
actually seen the product yet!  :) So, still it wouldn't hurt to have
delete set the pointer to zero, though it may not prevent *all*
multiple deallocations.

Joe Buck, in some mail he sent to me personally, points out that
setting the pointer to zero does incur a run-time cost of writing the
zeros and this might be a valid enough argument against such
functionality by default. Still the question is, if I wanted to design
a class which contained an operator delete which *DID* set pointers to
zero, could I even do this with C++ 2.0, given the standard prototype
of operator delete?

	Martin> To add the facility for 'delete' to set the pointer
	Martin> provided to NULL, would in effect impose a mechanism such
	Martin> as this on the language.  It is not a small scale change.

Maybe this is really more appropriate for comp.std.c++, but what are
the thoughts on changing the prototype for operator delete to:

	void operator delete( void *&p );

This change shouldn't break any existing code unless that code
depended on the value of p after it had been freed, and I can't think
of a reason why anyone would do this on purpose, given that p's value
after deletion is undefined anyway.  Also, this (minor) change should
give the class designer the ability to implement pointer zeroing if
desired.

-Barry