Topic: delete considered bad style! (was Why operator [] is not provided for auto_ptr in the standard C++ libaray?)
Author: Ian Haggard <ian@shellus.com>
Date: 1997/01/14 Raw View
Valentin Bonnard <bonnardv@pratique.fr> wrote:
> > The short answer is that having and operator T*() would make possible
> > travesties such as this:
> >
> > auto_ptr<Foo> bar(new Foo);
> > ...
> > delete bar; // bar is implicitly converted to Foo*, so this is legal
>
> But these programs are legal too.
>
> int* p = new int;
> delete p;
> delete p;
>
> int i;
> delete &i;
>
> char a[5];
> delete [] a; // not sure for this one
>
> {
> auto_ptr<Foo> bar(new Foo);
> bar.~auto_ptr<Foo> ();
> }
>
> The user has to know what's an auto_ptr in order to use it.
> I don't think the argument is very powerfull.
My advice would be to read some Stroustrup and some Meyers and then
you'll see the power of this argument. Actually, the examples you just
gave do an excellent job of both making a case for having an auto_ptr
(as well as having something like an auto_array_ptr) and for that
auto_ptr not having an implicit conversion. Note that in all of your
examples, the user is doing something that would cause what the standard
so politely terms "undefined behavior". One of the goals of the C++
standards designers (heck, of any program or language designer) is to
make it difficult, but not necessarily impossible, for the user to do
things that often result in undefined behavior. Each of these examples
is either bad style or an illustration of the utility of the auto_ptr
class as defined by the standard. Bad style, you say? What makes Ian
Haggard the ultimate arbiter of bad style? For this article, let me
define bad style as the use of a language construct that can result in
undefined behavior, especially undefined behavior that cannot be
detected at compile-time, when there is an alternate language construct
available that is harder to use in such a way as to cause undefined
behavior. In this case, the delete operator is a language construct
whose use frequently results in undefined behavior. And the auto_ptr
class is an alternate language construct that is harder to use in ways
that result in undefined behavior.
> int* p = new int;
> delete p;
> delete p;
The example using the variable "p" is a fairly common error in C++ that
I have dealt with. Usually it occurs when there is a function that
allocates an array that must be deleted at or before function exit and
the programmer writes some code that they think is going to exit but in
truth is not guaranteed to exit. Then they end up deleting the array
once before they think the function is about to exit and once when the
function actually exits. To correct this, the programmer should
re-declare the variable "p" to be an auto_ptr<int>. If "p" were an
array, then the programmer would want to re-declare it a vector<int> or
an auto_array_ptr (if the standard ends up defining one). Then none of
the delete's would be necessary, making the code cleaner, smaller, and
exception safe. Actually, the more common form of this error is where
the programmer just forgets altogether do delete the variable "p". And
this case, too, is taken care of by the new language constructs in the
standard.
> int i;
> delete &i;
The example with the variable "i" is just plain bad style. Even though
this is not a syntax error, it should should be patently obvious to the
person writing this that they are doing something screwy. Anyone who
takes the address of something and then deletes it should be shot. If
none of the C++ lint-like programs detect this horrendous construct,
then they should. The only case when I can even imagine that something
similar might not result in undefined behavior is if a function is
passed a reference to a heap object and needs to delete the object being
referred to. But this is still bad style -- if the function is going to
delete an object which is passed to it, that object should be passed as
a pointer. Or better yet, the object should be passed as an auto_ptr in
order to signal that the object is deletable and the function is being
given ownership of the object.
> char a[5];
> delete [] a; // not sure for this one
The example with the variable "a" is actually a good one. This is an
error that is not patently obvious to the programmer. However, if
programmers start using auto_ptr's, STL vector's, and possibly
auto_array_ptr's, then they will not be in such a rush to delete
everything in sight. I personally use these three constructs all over
in my programs (yes, I wrote my own auto_array_ptr template). And it's
been a LONG time (probably close to a year) since I've used delete other
than in the implementation of a generic container class, such as a
priority queue or disjoint set data structure.
> {
> auto_ptr<Foo> bar(new Foo);
> bar.~auto_ptr<Foo> ();
> }
As for the example using the variable "bar" and the explicit destructor
call, this is also generally bad style. The designers of C++ made it
legal to explicitly call an object's destructor in order to allow
flexibility in the language, but explicit destructor calls are one of
those constructs that should signal DANGER to any C++ programmer. There
are some cases when explicit destructor calls are useful. In the STL,
for example, the construct and destroy functions are implemented using
in-place construction and explicit destructor calls. These functions
and various allocator member-functions are used extensively in the STL
container classes. But again, I would question their use by anyone who
is not implementing a generic container class.
In summary, just because it's legal syntactically does not mean you want
to do it. In fact, it is highly desirable to avoid language constructs
which are legal syntactically but likely to result in undefined
behavior. The goal of the auto_ptr class is two-fold: exception safety
and enabling the user to reduce their usage of the delete operator,
which is perfectly legal syntactically but nonetheless error-prone. I
would even venture to say that the use of the delete operator is bad
style outside of the implementation of a generic container class. And
we have the forthcoming standard to thank for the fact that now the
normal applications programmer need hardly ever use delete.
--
Ian Haggard || ian@shellus.com (work) || IanHaggard@juno.com (home)
Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]