Boost logo

Boost :

From: Rob Stewart (stewart_at_[hidden])
Date: 2005-02-15 14:25:22


From: Chris Just <cpjust_at_[hidden]>
> Jonathan Turkanis wrote:
> >>>> I wanted to create a very simple AutoPtr that was essentially the
> >>>> same as the STL auto_ptr (and backwards compatible with it).
> >>>
> >>> Unfortunately, you haven't (see below). Furthermore, auto_ptr is
> >>> widely recognized as unsafe because it is too easy to transfer
> >>> ownership unintentionally.
> >>
> >> Of course you can unintentionally transfer ownership if you don't
> >> know what you're doing. ;)
> >
> > The problem is that the code which transfers ownership appears innocuous on the
> > surface, because auto_ptr allows transfer of ownership with the same syntax
> > ordinarily used for copying:
> >
> > void f()
> > {
> > auto_ptr<int> ptr(new int);
> > auto_ptr<int> ptr2(ptr); // ptr is now empty.
> > }
>
> Why would anyone in their right mind do that?

You wouldn't do that. He was simply illustrating the point with
a simple example. The point is, any time you construct one
auto_ptr from another, ownership is transferred.

> >> I believe that's why you're not supposed to pass an auto_ptr to a
> >> function -- instead you pass the pointer itself with .get(). You
> >> can't stop everyone from shooting themselves in the foot.
> >
> > Declaring a function to take an argument of type auto_ptr<...> is one way to
> > signal that ownership is being transfered to the function. It also ensures that
> > the managed object will be deleted, unless the function takes explicit steps to
> > cause a leak. Passing raw pointers defeats this system.
>
> Yes, this is true if you're calling a function that you wrote and compiled yourself, but if you're
> calling a 3rd party API that you didn't compile yourself, that can be dangerous (or so I've been
> told).

It is exactly the thing to do with any function if you intend to
transfer ownership of the memmory referenced by the point in or
out of a function. The mere fact that a function takes or
returns an auto_ptr is documenting and requiring the transfer of
ownership. You can't escape the transfer and that's the point.

Consider a function returning an auto_ptr. If someone calls the
function, ostensibly for some side effect of the call, and the
function returns a pointer to allocated memory, what happens if
the caller ignores the return value? The compiler doesn't offer
any help when you ignore a return value, so it is not hard to
write code that does so.

If the function returns a raw pointer to newly allocated memory,
the memory is leaked because the caller ignored it rather than
deleting it. OTOH, if the function returns an auto_ptr to that
memory, the memory will be released unless the caller
release()es it and then forgets to delete it. IOW, if the caller
ignores the return value of such a function, the temporary
auto_ptr it returns will go out of scope and delete the memory it
was given to manage.

The bottom line is that it is perfectly safe and quite desirable
to return an auto_ptr from a function.

Now consider a function taking an auto_ptr argument. That
function is quite clearly documenting that it is taking ownership
of the memory; it will delete the memory or transfer ownership to
yet another object that will manage the memory. The caller must
supply an auto_ptr as the constructor is explicit. Unfortunately
if the caller already has an auto_ptr, there's no information at
the call site that the transfer will occur. (That's what
Jonathan was trying to illustrate with his example above.
Looking just at the second line, there's nothing that screams
"ptr is losing ownership of its memory as a result of this
call.")

The bottom line is that it is not as desirable to take an
auto_ptr function argument from the caller's perspective, but it
is still appropriate if that's all you have at your disposal.
Then again, as Jonathan has been pointing out, you can use
move_ptr, the upcoming PBSP, or even the reference counted
shared_ptr, just to mention a few types in Boost.

> >> How? If you pass a pointer to a function, you shouldn't be deleting
> >> it inside that function anyways (so pass it with .get()),
> >
> > What if you want to transfer ownership to the function, as is often the case?
>
> Then pass ptr.release() to the function.

Then how does the function document that it is deleting the
memory and the caller had better not reference the memory after
the call? Comments aren't good at this, though taking an
auto_ptr argument is only slightly better.

> >> and if you
> >> return a pointer from a function, you can save it in a new AutoPtr so
> >> it will get deleted automatically...
> >
> > This depends on the calling code to do the right thing. If you return an
> > auto_ptr, the managed object will be freed even if the calling code choses to
> > ignore the return value.
>
> I agree, if it's your own code, return an auto_ptr, but if you're an API, you must return a raw
> pointer.

No, if the function returns allocated memory that the caller must
delete, then the function should return a smart pointer. In this
context, auto_ptr is exactly the right answer. In others,
shared_ptr might be what you want.

> >> I don't set my pointer to NULL when I release() it, I just set
> >> m_Owner = false.
> >
> > This behavior is not compatible with std::auto_ptr.
>
> I could change it to set the pointer to NULL, but this is what Microsoft's auto_ptr does, and it
> looked safer.

It permits clients of your smart pointer to reference dangling
pointers. That's not a good plan.

> As you might infer from some of my comments, I'm dealing mostly with API component code myself,
> which integrates some older sub components, so I have no choice but to pass raw pointers most of
> the time.

The best way to deal with such APIs is to wrap them in a safer
version that traffics in smart pointers. That way, only those
wrapper functions have to be scrutinized to ensure they are
correctly managing memory and all the rest of your code can avoid
raw pointers and the dangers they pose.

-- 
Rob Stewart                           stewart_at_[hidden]
Software Engineer                     http://www.sig.com
Susquehanna International Group, LLP  using std::disclaimer;

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