Exposition
Yesterday, Logan pointed out that you can construct a shared_ptr<T>
that manages some T
but shares a control block with some completely different shared_ptr<U>
:
template <typename T> class shared_ptr { public: // constructs a shared_ptr that shares ownership with r and stores p template<typename U> shared_ptr(shared_ptr<U> const &r, T *p); // ... };
This is a pretty cool use of shared_ptr
that I had not seen before.
A Compelling Example
Say you have two objects -- one nested inside the other -- that need to be able to refer to eachother. If you give each object a shared_ptr
to the other object, they'll leak-lock eachother until your program dies of consumption. You could break the symmetry by giving one a weak_ptr
to the other, but then you run the risk of one object expiring before the other.
What we really want is both objects to stay alive as long as either is around:
#include <memory> #include <iostream> struct Foo; struct Bar { Bar(Foo *f) : foo(f) { std::cout << "Bar\n"; } ~Bar() { std::cout << "~Bar\n"; } // goal: this Foo should be valid as long as this Bar is alive Foo *foo; }; struct Foo : std::enable_shared_from_this<Foo> { Foo() : bar(new Bar(this)) { std::cout << "Foo\n"; } ~Foo() { std::cout << "~Foo\n"; } // give someone access to our Bar std::shared_ptr<Bar> getBar() { return std::shared_ptr<Bar>(shared_from_this(), bar.get()); } std::unique_ptr<Bar> bar; }; int main() { std::shared_ptr<Bar> bar; { std::shared_ptr<Foo> foo(new Foo); bar = foo->getBar(); } std::cout << "foo is dead now...or is it?\n"; }
When run, the above code produces the following output:
Bar Foo foo is dead now...or is it? ~Foo ~Bar
The Thrilling Conclusion
By attaching the shared_ptr<Bar>
to the control block for the shared_ptr<Foo>
, we can enforce the idea that if we grab a Bar
from a Foo
, the Foo
will live at least as long as the Bar
does.