Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2002-09-21 11:06:17


From: "David Abrahams" <dave_at_[hidden]>
> From: "Peter Dimov" <pdimov_at_[hidden]>
>
> > No, reset() cannot be used to transfer ownership away from a scoped_ptr.
>
>
> No, but swap() can.
>
> > You
> > can only transfer ownership into a scoped_ptr, something that you do
with
> > the constructor as well.
>
> Almost. There's swap().

True. scoped_ptr::swap is "my fault". It still doesn't let you leak memory,
so it's not as bad as a release(); it's provided to support the "scoped_ptr
as a class member" usage, where auto_ptr's copy/assignment semantics lead to
implicitly generated members that are often (always?) not what is desired.
The motivation, to quote from earlier e-mails to Beman, Greg, and Darin:

"The documentation states that "one common usage of scoped_ptr is to
implement a handle/body idiom." One common usage of the handle/body idiom is
to achieve strong exception safety by using the "construct temporary and
swap" idiom; however, scoped_ptr doesn't have swap(), so users are forced to
use raw pointers."

"I recall that scoped_ptr wasn't originally supposed to be used as a member
of a class (hence the name 'scoped_ptr' - i.e. an auto variable.) It did
evolve to support such use (I support this decision.)

My point is that swap() is a requirement for scoped_ptr when used as a
member; it enables replacing raw owning pointers with scoped_ptr's.

The principal drawbacks of using a raw pointer as a member is that it has
default semantics of the "big three" that are usually incorrect. scoped_ptr
is a perfect solution to this problem, since it defines a correct destructor
and disables the copy constructor and the assignment operator by default,
forcing the class to implement the proper semantics.

The problem is that without swap(), it's not possible to implement a
copyable class with a scoped_ptr member. With swap(), it's trivial.

Another peculiarity is that swap() is a lower-level primitive than reset()
(reset can be implemented on top of swap.)

For a discussion on using a pointer member to achieve strong exception
safety, see:

http://www.gotw.ca/gotw/059.htm

(the Cargill Widget example.) Note how the code uses auto_ptr, but doesn't
use any auto_ptr "features." A swap-capable scoped_ptr is a perfect fit
here."

"(2) Using swap to make code strongly exception-safe:

We have a class T that provides an assignment operator with only the basic
exception-safety guarantee, that is, the object is in deletable but
unspecified state after an exception. T doesn't have 'swap' but it does have
a copy constructor.

We need to build U on top of T that is strongly exception-safe, i.e.
U::operator= has no effect if an exception is thrown.

This is achieved easily via the pimpl idiom:

class U
{
private:

    T * pt;

public:

    U(U const & rhs): pt(new T(rhs.pt)) {}

    ~U() { delete pt; }

    void swap(U & rhs) { std::swap(pt, rhs.pt); }

    U & operator= (U const & rhs) { U(rhs).swap(*this); }

};

(non-essential members not shown.)

The idiom requires using some kind of pointer to manage the T object. Raw
pointers do the job fine, but are error-prone, since it's easy to forget to
define the proper destructor, copy constructor, and assignment operator.
Moreover, if we put two pointer members in the class, the naive
implementation:

class U
{
private:

    T * pt;
    T * pt2;

public:

    U(U const & rhs): pt(new T(rhs.pt)), pt2(new T(rhs.pt2)) {}
};

can leak when the second new throws.

Using auto_ptr eliminates the second problem and supplies a proper
destructor; unfortunately, the compiler-generated copy constructor and
assignment operator still have unwanted semantics.

A swap-capable scoped_ptr is the best solution. It will automatically
prevent errors by making the containing class noncopyable by default (if no
suitable copy constructor and assignment operator are supplied.)"


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk