Topic: auto_ptr and strict ownership


Author: junk@markwrobel.dk (Mark Wrobel)
Date: Tue, 22 Feb 2005 18:56:43 GMT
Raw View
Hello!

This is a question about auto_ptr and strict ownership. The question is
inspired by the section "The Scoop on Non-Owning auto_ptrs" in gotw #25
at http://www.gotw.ca/gotw/025.htm

Here it is stated that the following is illegal because A does not own
the pointer anymore.

// (after having copied a to another auto_ptr)
cout << A->Value();

In the second draft proposal of auto_ptr this was legal, A just wouldn't
call delete when it went out of scope, thus avoiding double deletes.
Anyway, this has been illegal since 1998 and A's internal pointer is now
set to NULL wihtout a delete, hence enforcing strict ownership, where
only the owning auto_ptr is allowed to access the pointer.

Until recently I thought this meant only one auto_ptr could assume
ownership of a specific pointer.  I based this belive on the fact that

auto_ptr<Dummy> A;
A = new Dummy();

is not legal. Hence I belived there were no way to assign a pointer to a
auto_ptr after the auto_ptr had been initialized. However, I soon
discovered this:

Dummy d;

auto_ptr<Dummy> A;
A.reset(d); // A assumes ownership of d.

// establish a scope
if(1)
{
     auto_ptr<Dummy> B;
     B.reset(d); // B assumes ownership of d.
}

The example shows that more auto_ptr's can assume ownership of the same
pointer. Further, when B goes out of scope it deletes d and A is left
with a dead pointer. Hence a call like

A->member_of_dummy()

whould be flawed, when B goes out of scope. If I am not mistaking, the
whole point of section "The Scoop on Non-Owning auto_ptrs" in gotw #25
was to avoid a situation like this.

I think the world would be much simpler if there was no reset method.
Then this

1: void foo(auto_ptr<Dummy> d)
2: void foo(auto_ptr<Dummy>& d)

whould state, with no ambiguity, that (1) foo assumes ownership and (2)
that foo does not assumes ownership of the pointer, and there would be
no other auto_ptr's assuming ownership - hence no fear of double deletes
and dead pointers.

With the reset method there is no guarantee that only one auto_ptr
assumes ownership of a pointer, which in my view is bad.

Could anyone explain why the reset method is a part of the auto_ptr API,
when it seems to open a can of worms?

Ofcourse this is "no biggie" if one is diciplined enough not to allow a
naked new, but then its a matter of coding style and not a guarantee
from the API.


Best Regards,
Mark Wrobel



















---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: technews@kangaroologic.com ("Jonathan Turkanis")
Date: Tue, 22 Feb 2005 19:46:04 GMT
Raw View
Mark Wrobel wrote:
> Hello!

I recently vowed never to answer another post about auto_ptr, but here goes.;-)

> Dummy d;
>
> auto_ptr<Dummy> A;
> A.reset(d); // A assumes ownership of d.

This is wrong. reset() takes a pointer, not a reference.

> // establish a scope
> if(1)
> {
>      auto_ptr<Dummy> B;
>      B.reset(d); // B assumes ownership of d.
> }
>
> The example shows that more auto_ptr's can assume ownership of the
> same pointer.

This is a clear misuse of auto_ptr. To make sure this never happens, an instance
of auto_ptr should take ownership of an object immediately upon that object's
allocation, or by transfer of ownership from another auto_ptr.

Anyway, the following demonstates the same error:

  if(1)
  {
       auto_ptr<Dummy> B(d);
  }

> I think the world would be much simpler if there was no reset method.
> Then this
>
> 1: void foo(auto_ptr<Dummy> d)
> 2: void foo(auto_ptr<Dummy>& d)
>
> whould state, with no ambiguity, that (1) foo assumes ownership and

This is already true.

> (2) that foo does not assumes ownership of the pointer, and there
> would be no other auto_ptr's assuming ownership - hence no fear of
> double deletes and dead pointers.

What about

   void foo(auto_ptr<Dummy>& d)
   {
        auto_ptr<Dummy> ptr(d);
   }

?

If you want to guard again transfer of ownership, the correct declaration is

   2: void foo(const auto_ptr<Dummy>& d)

> With the reset method there is no guarantee that only one auto_ptr
> assumes ownership of a pointer, which in my view is bad.
>
> Could anyone explain why the reset method is a part of the auto_ptr
> API, when it seems to open a can of worms?

reset() is useful because it allows a single auto_ptr instance to be used many
times. Eliminating reset() wouldn't prevent the type of errors you mention.

> Ofcourse this is "no biggie" if one is diciplined enough not to allow
> a naked new, but then its a matter of coding style and not a guarantee
> from the API.

Not all aspects of a component's contract can be enforced by the language.

> Best Regards,
> Mark Wrobel

Jonathan


---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: junk@markwrobel.dk (Mark Wrobel)
Date: Tue, 22 Feb 2005 22:40:21 GMT
Raw View
Jonathan Turkanis wrote:
> Mark Wrobel wrote:
>
>>Hello!
>
>
> I recently vowed never to answer another post about auto_ptr, but here goes.;-)

I'm glad you took your time to answer ;-)

>>Dummy d;
>>
>>auto_ptr<Dummy> A;
>>A.reset(d); // A assumes ownership of d.
>
>
> This is wrong. reset() takes a pointer, not a reference.

So sorry. I read my mail very carefully, but somehow this error went
unnoticed. It should say:

Dummy* d
auto_ptr<Dummy> A;
A.reset(d);

But is see that you noticed the intention of my example anyway ;-).

>>// establish a scope
>>if(1)
>>{
>>     auto_ptr<Dummy> B;
>>     B.reset(d); // B assumes ownership of d.
>>}
>>
>>The example shows that more auto_ptr's can assume ownership of the
>>same pointer.
>
>
> This is a clear misuse of auto_ptr. To make sure this never happens, an instance
> of auto_ptr should take ownership of an object immediately upon that object's
> allocation, or by transfer of ownership from another auto_ptr.
>
> Anyway, the following demonstates the same error:
>
>   if(1)
>   {
>        auto_ptr<Dummy> B(d);
>   }

I agree! It is a misuse of auto_ptr. Thanks for the example that very
clearly shows that the problem of dual ownership isn't avoided by not
using auto_ptr::reset().


>>I think the world would be much simpler if there was no reset method.
>>Then this
>>
>>1: void foo(auto_ptr<Dummy> d)
>>2: void foo(auto_ptr<Dummy>& d)
>>
>>whould state, with no ambiguity, that (1) foo assumes ownership and
>
>
> This is already true.
>
>
>>(2) that foo does not assumes ownership of the pointer, and there
>>would be no other auto_ptr's assuming ownership - hence no fear of
>>double deletes and dead pointers.
>
>
> What about
>
>    void foo(auto_ptr<Dummy>& d)
>    {
>         auto_ptr<Dummy> ptr(d);
>    }
>
> ?

I must admit that I wrongfully thought of using instantiation only in
this context

auto_ptr<Dummy> ptr; and
auto_ptr<Dummy> ptr(new something);


> If you want to guard again transfer of ownership, the correct declaration is
>
>    2: void foo(const auto_ptr<Dummy>& d)
>

Yes ofcourse, and it's also a good thing to use the const keyword on
auto_ptr members of a class, to avoid theft of ownership. But perhaps
its a better strategy to overload operator=() for the class where
auto_ptr is a memeber?

>>With the reset method there is no guarantee that only one auto_ptr
>>assumes ownership of a pointer, which in my view is bad.
>>
>>Could anyone explain why the reset method is a part of the auto_ptr
>>API, when it seems to open a can of worms?
>
>
> reset() is useful because it allows a single auto_ptr instance to be used many
> times. Eliminating reset() wouldn't prevent the type of errors you mention.

Alright so reset() has it's place... My misconception led me to believe
that reset() was the only way to get dual ownership, and hence it's
presence in the API came with a rather large pricetag.

Since reset() deletes the previous pointer held by auto_ptr, the method
could be used when the auto_ptr is a member of a class. But perhaps not
so ideal in a loop - following the advice of limiting the scope of a
variable.

>>Ofcourse this is "no biggie" if one is diciplined enough not to allow
>>a naked new, but then its a matter of coding style and not a guarantee
>>from the API.
>
>
> Not all aspects of a component's contract can be enforced by the language.

So true... A hammer is a god tool for driving nails - but it could also
hit your fingers, which is a misuse of the tool. To limit the use of the
hammer so that it only hits nails, might be good for your fingers, but
would most propably render the tool useless to operate. ;-)

/Mark





---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: johnchx2@yahoo.com
Date: Tue, 22 Feb 2005 16:40:14 CST
Raw View
Mark Wrobel wrote:

> With the reset method there is no guarantee that only one auto_ptr
> assumes ownership of a pointer, which in my view is bad.

This is true with or without reset().  You can initialize any number of
auto_ptrs with the same pointer if you've got a mind to:

int* p = new int (0);
std::auto_ptr<int> ap1 (p);
std::auto_ptr<int> ap2 (p);         // problem 1
std::auto_ptr<int> ap3;
ap3 = std::auto_ptr<int> (p);       // problem 2
std::auto_ptr<int> ap4 (ap1.get()); // problem 3
int* p2 = ap1.operator->();         // problem 4a
int* p3 = &(*ap1);                  // problem 4b

Problems 4a and 4b are my personal favorites, since they demonstrate
that even if you (a) don't store the original pointer and (b) eliminate
get(), you can STILL extract a bare pointer from an auto_ptr, at which
point all bets are off.

As far as function signatures are concerned, I would think that
functions that mention auto_ptr (as a parm) but don't take ownership
would be very rare.  In most cases, why would you want an auto_ptr<T>?
Why wouldn't a T& suffice?  In other words, why does the function care
about the storage class and ownership of the object if it doesn't plan
to take ownership of it?  There are probably some special cases where
this would be required, but I'd expect they'd be few and far between.

Of course, I may be missing something obvious.  It's been known to
happen...

---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]