In this post I want to demonstrate how C++11
smart pointers can be abused to track the lifetime of an object
without taking ownership of them. Due to the resemblance to
std::enable_shared_from_this I call this
technique enable_weak_from_this. One word of warning though, this
technique is a crude hack and can lead to dangling pointers
and undefined behavior. You should NOT use it in
production code.
Intent
Allow an object t whose lifetime is managed by another object (which is
not a smart pointer) to create smart references to itself that know
whether t still exists or if it has been deleted.
Implementation
The core of enable_weak_from_this is a std::shared_ptr
private member variable that is constructed with the this pointer and a
custom deleter that does not delete the "managed" pointer on desctruction.
At first, this might seem pointless since the job of shared_ptr is to
delete pointers when there are no other shared_ptr that reference them.
But what remains is the reference counting mechanism which can be used to
track the lifetime of t. The shared_ptr member of t guarantees that
during its lifetime the reference count is always equal or greater
than one. After t is deleted the reference count drops to zero. So the
reference counter of the private shared_ptr member "tracks" the
lifetime of t.
To create smart references from t to itself, there is a public method
that returns a std::weak_ptr which is constructed from the
shared_ptr member. These non-owning weak references now allow to access
t (via a prior conversion to a shared_ptr) during its lifetime.
Here is the complete definition of
enable_weak_from_this:
Sample Code
And here is a small example that uses enable_weak_from_this, which I'll
explain below:
A inherits from enable_weak_from_this<A> and therefore objects of type
A are able to create those smart references to themselves. A also
has a method to create a "child" object of type B which is constructed
with a weak reference to its creator. All the usage of the weak reference
is in B::callHello where weak_ptr::lock() is used to create a temporary
shared_ptr<A> of the creator object if it still exists. If the creator
has been deleted, lock() will return an empty shared_ptr.
Finally main demonstrates what happens on b->callHello() calls before
and after the creator of b is deleted.
Problems
It is possible to create a shared_ptr of t whose lifetime exceeds
the lifetime of t by storing the return value of weak_ptr::lock().
So care must be taken that t is not deleted while using the acquired
shared_ptr.