Topic: auto_ptr::reset() redundant, unsafe?
Author: Martin D Kealey <martin@kcbbs.gen.nz>
Date: 1996/11/30 Raw View
I have have observed the "great auto_ptr debate" over the last while,
and it seems to me that the original simple intent has been somewhat
overrun by 'creeping featuritis'. Probably what I have to say is rather
too late to make make any impact on the standard, but anyway...
To get back to the original intent -- enforcing the deletion of a dynamic
object when its owner's lifetime ends -- I would like to suggest that the
idea of copying an auto_ptr is a little non-sensical: an object should be
owned by at most one instance of an auto_ptr, and an auto_ptr should
behave like an ordinary pointer where possible -- either it's "NULL", or
it points at (and therefore owns) an object.
If you want a pointer that doesn't exclusively own something, then you
want a regular C pointer (which doesn't own anything) or a counted-
pointer (which shares ownership); or possibly something more specialised
that will be written on a case-by-case basis (and therefore has no place
in the STL).
I suggest that copy semantics between auto_ptr's should be removed -- no
auto_ptr<T>::operator=(auto_ptr&) and no auto_ptr<T>::auto_ptr(auto_ptr&)
-- and something including the functionality of auto_ptr<T>::reset(T*) be
re-instated.
In article <57hprp$hgp@bmtlh10.bnr.ca> John Hicken wrote:
> reset() can fail if *this owns an object and an uncaught exception is
> thrown during the owned object's destruction.
reset() bundled the assignment of a new pointer with the deletion of the
previously owned object; the answer is not to delete this functionality,
but to separate it, but in a way that they can't be ignored:
void auto_ptr<X>::replace( X*&old_value, X*new_value );
delete old_value;
where you are obliged to do something with the old value -- delete it
presumably -- while still being able to assign a new pointer to the
auto_ptr.
If that looks too unpalatable, *maybe* we simply return the old pointer:
X* auto_ptr<X>::replace( X*new_value );
but although this looks simpler, it's easier to overlook the return
value, losing safety.
Removal of the various "copy" operators is going to make a straight-out
assignment look more complicated, but I suggest that it would highlight
more closely what is going on:
auto_ptr<X> p1 = new X;
auto_ptr<X> p2 = new X;
...
X * old;
p1.replace( old, p2.get() );
delete old;
or in the return-the-old-value version, just
delete p1.replace( p2.get() );
-Martin.
---
[ 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 ]
Author: andrewb@graphsoft.com (Andrew C. Bell)
Date: 1996/12/02 Raw View
john (j.d.) hickin (hickin@nortel.ca) wrote:
>reset() can fail if *this owns an object and an uncaught exception is
>thrown during the owned object's destruction.
What fails? Assuming we do:
void
auto_ptr<Type>::reset(Type *newPtr)
{
Type *oldPtr = ptr_;
ptr_ = newPtr;
delete oldPtr;
}
What's the problem? The auto_ptr is in a valid state even if the
destructor throws. The only potential problem I can see is that if
the auto_ptr is destroyed during stack unwinding and the destructor of
newPtr also throws, then the program will terminate() there and then
due to the double exceptions. If that is the problem, how is
assignment from an existing auto_ptr any better?
Personally, I'd prefer auto_ptr's copy constructor to do:
auto_ptr<Type>::auto_ptr(const auto_ptr<Type> &newPtr)
{
ptr_ = newPtr->ptr_? new Type(&newPtr->ptr_) : NULL;
}
That way you can safely have auto_ptrs copied in default copy
construction for any Type that has a public copy constructor.
Andrew Bell
andrewb@ graphsoft.com, etc.
[ 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 ]
Author: "john (j.d.) hickin" <hickin@nortel.ca>
Date: 1996/12/04 Raw View
>What fails? Assuming we do:
>
>void
>auto_ptr<Type>::reset(Type *newPtr)
>{
> Type *oldPtr = ptr_;
> ptr_ = newPtr;
> delete oldPtr;
>}
Yes, that would seem to do the job. In view of the fact that the code for
operator= must be similar I'm not quite sure how I came up with that gem of
misinformation.
--
John Hickin Nortel Technology, Montreal, Quebec
(514) 765-7924 hickin@nortel.ca
---
[ 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 ]
Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1996/11/27 Raw View
Ben Liblit <ben@ls.com> wrote in article <6xpw10ie84.fsf@casper.ls.com>...
> I understand that auto_ptr::reset() has been eliminated [because]
> it was considered unsafe and redundant.
> I'm not convinced...
>
> [Here] is an example where auto_ptr::reset() would
> prove useful. Suppose we are using an auto_ptr to cache some lazily
> allocated thing. Initially, we do not create this thing, so the
> auto_ptr is constructed owning nothing:
>
> auto_ptr< Thing > cache;
>
[Without reset(), how do we reassign 'cache'?]
>
> cache = auto_ptr< Thing >( new Thing );
>
> I hope that I am not alone in finding this inelegant and cumbersome to
> maintain.
It's just my humble opinion, but I don't find that syntax inelegant or
cumbersome. In fact, it says exactly what it's doing: assigning a new,
automatically-managed pointer to 'cache'. The syntax reinforces what's
going on, thus making maintenance easier: it automatically raises the
question "So what was the previous value?" and makes it clear that the new
auto_ptr is a temporary (losing ownership, and therefore unimportant). I
think you've stumbled upon the "right" way of doing this. Given that I like
the way it works, I don't see a need for a redundant way of doing it,
unsafe or not. Perhaps that's how the committee felt too. I would still
like to hear why reset() is unsafe, although just out of curiousity. I
don't mind the new way.
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~bradds
---
[ 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 ]
Author: Ben Liblit <ben@ls.com>
Date: 1996/11/27 Raw View
Once upon a time, the auto_ptr template class had a reset() method.
This method let you explicitly change the pointer "owned" by an
auto_ptr instance.
I understand that auto_ptr::reset() has been eliminated. I have found
one reference that states that it was considered unsafe and redundant
with respect to the copy assignment operator. I'm not convinced that
either one of these assertions is true. Can someone provide a
compelling example of auto_ptr::reset() unsafety?
By way of contrast, here is an example where auto_ptr::reset() would
prove useful. Suppose we are using an auto_ptr to cache some lazily
allocated thing. Initially, we do not create this thing, so the
auto_ptr is constructed owning nothing:
auto_ptr< Thing > cache;
Later on, we determine that we must create the thing. We will cache
it for later use:
if (!cache.get())
cache.reset( new Thing ); // no longer legal
Now that's pretty concise, but without auto_ptr::reset(), it's no
good. What are our alternatives?
cache = new Thing; // illegal
Straight assignment of the pointer does not work. As far as I know,
auto_ptr has no assignment-from-a-pointer operator. The only thing
you can assign is another compatible auto_ptr. A temporary auto_ptr
will *not* be constructed implicitly, as the build-from-a-pointer
constructor is declared "explicit". That leaves us with no choice but
the following:
cache = auto_ptr< Thing >( new Thing );
I hope that I am not alone in finding this inelegant and cumbersome to
maintain.
Ok, so there's one obvious example of how auto_ptr::reset() would be
handy. Can someone provide a good counterexample showing how it can
create more trouble than it is worth?
---
[ 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 ]
Author: "john (j.d.) hickin" <hickin@nortel.ca>
Date: 1996/11/27 Raw View
> [text elided] Can someone provide a good counterexample showing how it can
> create more trouble than it is worth?
reset() can fail if *this owns an object and an uncaught exception is thrown
during the owned object's destruction.
--
John Hickin Nortel Technology, Montreal, Quebec
(514) 765-7924 hickin@nortel.ca
[ 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 ]