When would you use an std::auto_ptr instead of boost::shared_ptr?
-
22-07-2019 - |
Question
We've pretty much moved over to using boost::shared_ptr
in all of our code, however we still have some isolated cases where we use std::auto_ptr
, including singleton classes:
template < typename TYPE >
class SharedSingleton
{
public:
static TYPE& Instance()
{
if (_ptrInstance.get() == NULL)
_ptrInstance.reset(new TYPE);
return *_ptrInstance;
}
protected:
SharedSingleton() {};
private:
static std::auto_ptr < TYPE > _ptrInstance;
};
I've been told that there's a very good reason why this hasn't been made a shared_ptr
, but for the life of me I can't understand why? I know that auto_ptr
will eventually get marked as depreciated in the next standard, so I'd like to know what/how I can replace this implementation.
Also, are there any other reasons why you'd consider using an auto_ptr
instead of a shared_ptr
? And do you see any problems moving to shared_ptr in the future?
Edit:
- So in answer to "can I safely replace
auto_ptr
withshared_ptr
in the above code", the answer is yes - however I'll take a small performance hit. - When
auto_ptr
is eventually marked as depreciated and we move over tostd::shared_ptr
, we'll need to thoroughly test our code to make sure we're abiding by the different ownership semantics.
Solution
auto_ptr
and shared_ptr
solve entirely different problems. One does not replace the other.
auto_ptr
is a thin wrapper around pointers to implement RAII semantics, so that resources are always released, even when facing exceptions. auto_ptr
does not perform any reference counting or the like at all, it does not make multiple pointers point to the same object when creating copies. In fact, it's very different. auto_ptr
is one of the few classes where the assignment operator modifies the source object. Consider this shameless plug from the auto_ptr wikipedia page:
int *i = new int;
auto_ptr<int> x(i);
auto_ptr<int> y;
y = x;
cout << x.get() << endl; // Print NULL
cout << y.get() << endl; // Print non-NULL address i
Note how executing
y = x;
modifies not only y but also x.
The boost::shared_ptr
template makes it easy to handle multiple pointers to the same object, and the object is only deleted after the last reference to it went out of scope. This feature is not useful in your scenario, which (attempts to) implement a Singleton. In your scenario, there's always either 0 references to 1 reference to the only object of the class, if any.
In essence, auto_ptr
objects and shared_ptr
objects have entirely different semantics (that's why you cannot use the former in containers, but doing so with the latter is fine), and I sure hope you have good tests to catch any regressions you introduced while porting your code. :-}
OTHER TIPS
Others have answered why this code uses an auto_ptr
instead of a shared_ptr
. To address your other questions:
What/how I can replace this implementation?
Use either boost::scoped_ptr
or unique_ptr
(available in both Boost and the new C++ standard). Both scoped_ptr
and unique_ptr
provide strict ownership (and no reference counting overhead), andthey avoid the surprising delete-on-copy semantics of auto_ptr
.
Also, are there any other reasons why you'd consider using an auto_ptr
instead of a shared_ptr
? And do you see any problems moving to shared_ptr
in the future?
Personally, I would not use an auto_ptr
. Delete-on-copy is just too non-intuitive. Herb Sutter seems to agree. Switching to scoped_ptr
, unique_ptr
, or shared_ptr
should offer no problems. Specifically, shared_ptr
should be a drop-in replacement if you don't care about the reference counting overhead. scoped_ptr
is a drop-in replacement if you aren't using auto_ptr
's transfer-of-ownership capabilities. If you are using transfer-of-ownership, then unique_ptr
is almost a drop-in replacement, except that you need to instead explicitly call move
to transfer ownership. See here for an example.
auto_ptr is the only kind of smart pointer I use. I use it because I don't use Boost, and because I generally prefer my business/application-oriented classes to explicitly define deletion semantics and order, rather than depend on collections of, or individual, smart pointers.