tags:

views:

326

answers:

4
+1  Q: 

auto_ptr design

In my opinion, a class should provide a well defined abstraction and no private members should be modified without the knowledge of class. But when I checked the "auto_ptr" (or any other smart pointer), this rule is violated. Please see the following code

class Foo{
public:
   Foo(){}
};

int main(int argc, char* argv[])
{
   std::auto_ptr<Foo> fooPtr(new Foo);
   delete fooPtr.operator ->();
   return 0;
}

The operator overload (->) gives the underlying pointer and it can be modified without the knowledge of "auto_ptr". I can't think it as a bad design as the smart pointers are designed by C++ geeks, but I am wondering why they allowed this. Is there any way to write a smart pointer without this problem.

Appreciate your thoughts.

+2  A: 

No, there's no way to completely prohibit such bad usage in C++.

As a general rule, the user of any library code should never call delete on any wrapped pointers unless specifically documented. And in my opinion, all modern C++ code should be designed so that the user of the classes never was left the full responsibility to manually release her acquired resources (ie. use RAII instead).

Aside note: std::auto_ptr<T> isn't the best option anymore. Its bad behaviour on copying can lead to serious coding errors. Often a better idea is to use std::tr1::scoped_ptr<T> or std::tr1::shared_ptr<T> or their Boost variants instead.

Moreover, in C++0x, std::unique_ptr<T> will functionally supercede std::auto_ptr<T> as a safer-to-use class. Some discussion on the topic and a recent C++03 implementation for unique_ptr emulation can be found here.

pyrtsa
Thanks. I was using auto_ptr just for explanation. I am using boost::shared_ptr. RAII looks interesting
Appu
Not sure why this answer was downvoted...
j_random_hacker
+4  A: 

In order to provide fast, convenient, "pointer-like" access to the underlying object, operator-> unfortunately has to "leak" its abstraction a bit. Otherwise, smart pointers would have to manually wrap all of the members that are allowed to be exposed. These either requires a lot of "configuration" work on the part of those instantiating the smart pointer, or a level of meta-programming that just isn't present in C++. Besides, as pyrsta points out, even if this hole was plugged, there are still many other (perhaps non-standard) ways to subvert C++'s access control mechanisms.

Dave Ray
That makes perfect sense. Thanks
Appu
+2  A: 

Is there any way to write a smart pointer without this problem.

It isn't easy, and generally no (i.e., you can't do it for every, general Foo class).

The only way I can think of, to do this, would be by changing the declaration of the Foo class: make the Foo destructor private (or define a private delete operator as a member of the Foo class), and also specify in the declaration of the Foo class that std::auto_ptr<Foo> is a friend.

ChrisW
Yeah that is the only way I think. But the Foo has to be modified and all classes which will be used with auto_ptr. That is going to be overkill.
Appu
People who use std::auto_ptr must know what it is, and how to use it. Sometimes, C++ makes bad things impossible; but sometimes it can't, and instead it only makes good things easy (where here the "good thing" is "remembering to delete the object", and the bad thing is "deleting it more than once").
ChrisW
In C++, the developer is left all the tools to break the program. The best thing to do is to document the behaviour as well as the proper usage of one's code. And to follow the best practices as documented elsewhere. ;)
pyrtsa
+3  A: 

There are two desirable properties a smart pointer should have:

  1. The raw pointer can be retrieved (e.g. for passing to legacy library functions)
  2. The raw pointer cannot be retrieved (to prevent double-delete)

Obviously, these properties are contradictory and cannot be realised at the same time! Even Boost's shared_ptr<Foo> et al. have get(), so they have this "problem." In practice, the first is more important, so the second has to go.

By the way, I'm not sure why you reached for the slightly obscure operator->() when the ordinary old get() method causes the same problem:

std::auto_ptr<Foo> fooPtr(new Foo);
delete fooPtr.get();
j_random_hacker