19

When trying to use an auto_ptr with a type that was declared with forward-declaration, like this:

class A;
...
std::auto_ptr<A> a;

the destructor of A is not called (apparently, because auto_ptr internally deletes the underlying pointer and the destructor for an incomplete type cannot be called).

However, the same code works fine and the destructor is called when using std::shared_ptr instead of std::auto_ptr. How can that be explained?

Ruggero Turra
  • 16,929
  • 16
  • 85
  • 141

1 Answers1

36

A shared_ptr can be declared with an incomplete type, yes. The type does not need to be complete until you initialize or reset it.

When you initialize or reset a shared_ptr to point to a new object, it creates a "deleter" that can be used to destroy the object. For example, consider the following:

// somewhere where A is incomplete:
std::shared_ptr<class A> p;

// define A
class A { /* ... */ };

p.reset(new A());

When you call reset, A is complete because you're creating an instance of it using new. The reset function creates and stores internally a deleter that will be used to destroy the object using delete. Because A is complete here, that delete will do the right thing.

By doing this, shared_ptr does not require that A be complete when the shared_ptr<A> is declared; it only requires that A is complete when the shared_ptr constructor that takes a raw pointer is invoked or when you call reset with a raw pointer.

Note that if A is not complete when you do one of those two things, shared_ptr won't do the right thing and the behavior is undefined (this is explained in the documentation for boost::shared_ptr, which is probably the best resource for learning how to use shared_ptr correctly, regardless which version of shared_ptr you are using (Boost, TR1, C++0x, etc.)).

However, as long as you always follow the best usage practices for shared_ptr--notably, if you always initialize and reset a shared_ptr directly with a pointer resulting from a call to new--you won't have to worry about violating this rule.

This functionality isn't free: shared_ptr has to create and store a pointer to the deleter functor; typically this is done by storing the deleter as part of the block that stores the strong and weak reference counts or by having a pointer as part of that block that points to the deleter (since you can provide your own deleter).

auto_ptr (and unique_ptr too) is designed to be free of any overhead: operations on it are supposed to be just as efficient as using a dumb pointer. Thus, auto_ptr doesn't have this functionality.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 7
    Here is a summary of the completeness requirements for both unique_ptr and shared_ptr: http://home.roadrunner.com/~hinnant/incomplete.html – Howard Hinnant Apr 09 '11 at 19:29
  • 2
    For one hour of highly entertaining material on shared_ptr, check out [STL on STL](http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-1-of-n). Also worth mentioning `std::make_shared`. – Kerrek SB Apr 09 '11 at 23:38
  • 2
    Thanks! Here is an updated link for the summary of the completeness requirements for both unique_ptr and shared_ptr: http://howardhinnant.github.io/incomplete.html – Howard Hinnant Mar 19 '15 at 00:16