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