I ran across enable_shared_from_this
while reading the Boost.Asio examples and after reading the documentation I am still lost for how this should correctly be used. Can someone please give me an example and explanation of when using this class makes sense.
6 Answers
It enables you to get a valid shared_ptr
instance to this
, when all you have is this
. Without it, you would have no way of getting a shared_ptr
to this
, unless you already had one as a member. This example from the boost documentation for enable_shared_from_this:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
The method f()
returns a valid shared_ptr
, even though it had no member instance. Note that you cannot simply do this:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
The shared pointer that this returned will have a different reference count from the "proper" one, and one of them will end up losing and holding a dangling reference when the object is deleted.
enable_shared_from_this
has become part of C++ 11 standard. You can also get it from there as well as from boost.
from Dr Dobbs article on weak pointers, I think this example is easier to understand (source: http://drdobbs.com/cpp/184402026):
...code like this won't work correctly:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Neither of the two shared_ptr
objects knows about the other, so both will try to release the resource when they are destroyed. That usually leads to problems.
Similarly, if a member function needs a shared_ptr
object that owns the object that it's being called on, it can't just create an object on the fly:
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
This code has the same problem as the earlier example, although in a more subtle form. When it is constructed, the shared_pt
r object sp1
owns the newly allocated resource. The code inside the member function S::dangerous
doesn't know about that shared_ptr
object, so the shared_ptr
object that it returns is distinct from sp1
. Copying the new shared_ptr
object to sp2
doesn't help; when sp2
goes out of scope, it will release the resource, and when sp1
goes out of scope, it will release the resource again.
The way to avoid this problem is to use the class template enable_shared_from_this
. The template takes one template type argument, which is the name of the class that defines the managed resource. That class must, in turn, be derived publicly from the template; like this:
struct S : enable_shared_from_this<S>
{
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->not_dangerous();
return 0;
}
When you do this, keep in mind that the object on which you call shared_from_this
must be owned by a shared_ptr
object. This won't work:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
Here's my explanation, from a nuts and bolts perspective (top answer didn't 'click' with me). *Note that this is the result of investigating the source for shared_ptr and enable_shared_from_this that comes with Visual Studio 2012. Perhaps other compilers implement enable_shared_from_this differently...*
enable_shared_from_this<T>
adds a private weak_ptr<T>
instance to T
which holds the 'one true reference count' for the instance of T
.
So, when you first create a shared_ptr<T>
onto a new T*, that T*'s internal weak_ptr gets initialized with a refcount of 1. The new shared_ptr
basically backs onto this weak_ptr
.
T
can then, in its methods, call shared_from_this
to obtain an instance of shared_ptr<T>
that backs onto the same internally stored reference count. This way, you always have one place where T*
's ref-count is stored rather than having multiple shared_ptr
instances that don't know about each other, and each think they are the shared_ptr
that is in charge of ref-counting T
and deleting it when their ref-count reaches zero.
There is one case where I find enable_shared_from_this extremely useful: Thread safety when using asynchronous callback.
Imagine class Client has a member of type AsynchronousPeriodicTimer:
struct AsynchronousPeriodicTimer
{
// call this periodically on some thread...
void SetCallback(std::function<void(void)> callback);
void ClearCallback(); // clears the callback
}
struct Client
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
: _timer(timer)
{
_timer->SetCallback(
[this]
()
{
assert(this); // what if 'this' is already dead because ~Client() has been called?
std::cout << ++_counter << '\n';
}
);
}
~Client()
{
// clearing the callback is not in sync with the timer, and can actually occur while the callback code is running
_timer->ClearCallback();
}
int _counter = 0;
std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}
int main()
{
auto timer = std::make_shared<AsynchronousPeriodicTimer>();
{
auto client = std::make_shared<Client>(timer);
// .. some code
// client dies here, there is a race between the client callback and the client destructor
}
}
The client class subscribes a callback function to the periodic timer. Once the client object goes out of scope, there is a race condition between the callback and the destructor. The callback may be invoked with a dangling pointer!
The solution: use enable_shared_from_this:
struct Client : std::enable_shared_from_this<Client>
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
: _timer(timer)
{
}
void Init()
{
auto captured_self = weak_from_this(); // weak_ptr to avoid cyclic references with shared_ptr
_timer->SetCallback(
[captured_self]
()
{
if (auto self = captured_self.lock())
{
// 'this' is guaranteed to be non-nullptr. we managed to promote captured_self to a shared_ptr
std::cout << ++self->_counter << '\n';
}
}
);
}
~Client()
{
_timer->ClearCallback();
}
int _counter = 0;
std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}
The Init
method is separated from the constructor since the initialization process of enable_shared_from_this
is not finalized until the constructor exits. Hence the extra method. It is generally unsafe to subscribe an asynchronous callback from within a constructor since the callback may access uninitialized fields.
It's exactly the same in c++11 and later: It is to enable the ability to return this
as a shared pointer since this
gives you a raw pointer.
in other word, it allows you to turn code like this
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
into this:
class Node : std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> getParent const() {
std::shared_ptr<Node> parent = m_parent.lock();
if (parent) {
return parent;
} else {
return shared_from_this();
}
}
private:
std::weak_ptr<Node> m_parent;
};