422

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.

John
  • 2,963
  • 11
  • 33
fido
  • 5,566
  • 5
  • 24
  • 20
  • 1
    The simple explanation is here: https://en.cppreference.com/w/cpp/memory/enable_shared_from_this – Fedor Jun 16 '21 at 06:43

6 Answers6

431

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.

brooksrelyt
  • 3,925
  • 5
  • 31
  • 54
1800 INFORMATION
  • 131,367
  • 29
  • 160
  • 239
  • 240
    +1. The key point is that the "obvious" technique of just returning shared_ptr(this) is broken, because this winds up creating multiple distinct shared_ptr objects with separate reference counts. For this reason you must never create more than one shared_ptr **from the same raw pointer**. – j_random_hacker Apr 03 '09 at 02:31
  • 7
    It should be noted that in _C++11 and later_, it is **perfectly valid** to use a `std::shared_ptr` constructor on a _raw pointer_ **if** it inherits from `std::enable_shared_from_this`. **I don' t know if** Boost's semantics were updated to support this. – Matthew Oct 10 '17 at 20:51
  • 7
    @MatthewHolder Do you have a quote for this? On cppreference.com I read "Constructing a `std::shared_ptr` for an object that is already managed by another `std::shared_ptr` will not consult the internally stored weak reference and thus will lead to undefined behavior." (http://en.cppreference.com/w/cpp/memory/enable_shared_from_this) – Thorbjørn Lindeijer May 01 '18 at 14:01
  • 14
    Why can't you just do `shared_ptr q = p`? – Dan M. May 08 '18 at 14:28
  • @DanM. AFAIK you can. I don't get the use case wither – HankTheTank Jan 02 '19 at 19:24
  • 3
    @DanM. You can, that's why this sample is not very useful. There are definitely use cases for it though. When there's no `q` and you need a `p` from inside the class. – Hatted Rooster Jan 05 '19 at 14:23
  • 2
    @DanM. yes you can do that because p and q are both shared_ptr. enable_shared_from_this is required when you are inside the class, e.g. in the method f() that I had above. It is a simplified example, but it shows the essentials – 1800 INFORMATION Jan 21 '19 at 04:20
  • 3
    @ThorbjørnLindeijer, you are right, it's C++17 and later. Some implementations did follow the C++16 semantics before it was released. The proper handling for C++11 to C++14 should be to use `std::make_shared`. – Matthew Mar 07 '19 at 20:04
  • So is this basically the same as boost::intrusive_ptr ? What is the difference ? – jcxz Jul 26 '19 at 14:03
  • ^ all the complications listed above, are reasons to not use these smart pointers. If the solution is more complex than the problem, what's the point? Sometimes these smart pointers simplify the code over new/delete, but sometimes they over complicate it. It makes training harder, understanding code harder, code bulkier, all things that increase chance of bugs (or increase maintenance costs) on a large code base). Just call 'new' in your Add(), and 'delete' in your Remove() and destructor, it was never that hard in the first place. – diox8tony Jun 06 '23 at 15:44
280

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_ptr 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
}
underscore_d
  • 6,309
  • 3
  • 38
  • 64
Artashes Aghajanyan
  • 2,899
  • 1
  • 15
  • 4
  • 26
    Thanks, this illustrates the problem being solved better than the currently accepted answer. – goertzenator May 02 '13 at 14:04
  • 2
    +1: Good answer. As an aside, instead of `shared_ptr sp1(new S);` it may be preferred to use `shared_ptr sp1 = make_shared();`, see for example http://stackoverflow.com/questions/18301511/stdshared-ptr-initialization-make-sharedfoo-vs-shared-ptrtnew-foo – Arun Apr 01 '15 at 20:27
  • 9
    I'm pretty sure the last line should read `shared_ptr sp2 = p->not_dangerous();` because the pitfall here is that you **must create a shared_ptr the normal way before you call `shared_from_this()` the first time!** This is really easy to get wrong! Before C++17 it is **UB** to call `shared_from_this()` before exactly one shared_ptr has been created the normal way: `auto sptr = std::make_shared();` or `shared_ptr sptr(new S());`. Thankfully from C++17 onwards doing so will throw. – AnorZaken Sep 01 '16 at 15:48
  • 2
    BAD Example: `S* s = new S(); shared_ptr ptr = s->not_dangerous();` <-- [It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr. Otherwise the behavior is undefined (until C++17)std::bad_weak_ptr is thrown (by the shared_ptr constructor from a default-constructed weak_this) (since C++17).](http://en.cppreference.com/w/cpp/memory/enable_shared_from_this). So the reality is that it should be called `always_dangerous()`, because you need the knowledge of if it has been shared already or not. – AnorZaken Sep 01 '16 at 15:56
  • 2
    @AnorZaken Good point. It would've been useful if you had submitted an edit request to make that fix. I've just done so. The other useful thing would've been for the poster not to choose subjective, context-sensitive method names! – underscore_d Dec 12 '16 at 10:06
  • This is a brilliant answer, absolutely brilliant! – SexyBeast Oct 19 '17 at 13:09
  • Thanks, this explains the technique well, but still don't quite understand why we need this in real sceanrio, do you have a actual example? – Baiyan Huang May 15 '21 at 01:41
  • This answer, as well as the accepted one, fails to provide a real-life example. The convoluted `shared_ptr sp2 = sp1->not_dangerous();` can be simply replaced with `shared_ptr sp2 = sp1;`. – Super-intelligent Shade Nov 01 '22 at 21:59
39

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.

mackenir
  • 10,801
  • 16
  • 68
  • 100
  • 2
    This is correct, and the really important part is `So, when you first create...` because that is a **requirement** (as you say the weak_ptr isn't initialized until you pass the objects pointer into a shared_ptr ctor!) and this requirement is where things can go horribly wrong if you are not careful. If you create no shared_ptr before calling `shared_from_this` you get UB - likewise if you create more than one shared_ptr you get UB too. You have to somehow make sure you create a shared_ptr _exactly_ once. – AnorZaken Sep 01 '16 at 16:12
  • 3
    In other words the whole idea of `enable_shared_from_this`is brittle to begin with since the point is to be able to get a `shared_ptr` from a `T*`, but in reality when you get a pointer `T* t` it is generally not safe to assume anything about it already being shared or not, and making the wrong guess is UB. – AnorZaken Sep 01 '16 at 16:17
  • "_internal weak_ptr gets initialized with a refcount of 1_" weak ptr to T are non owning smart ptr to T. A weak ptr is a owning smart ref to enough information to make a owning ptr that is a "copy" of other owning ptr. A weak ptr has no ref count. It has access to a ref count, like all owning ref. – curiousguy Nov 28 '17 at 23:43
21

There is one particular 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 client's callback and the client's destructor. The callback may be invoked with a dangling pointer!

The solution: using enable_shared_from_this to extend the object lifetime for the duration of the callback invocation.

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()
    {
        // the destructor cannot be called while the callback is running. shared_ptr guarantees this
        _timer->ClearCallback();
    
    }
    int _counter = 0;
    std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}

The mechanism of enable_shared_from_this, combined with the inherent thread safety of std::shared_ptr reference counting, enable us to ensure that the Client object cannot be destructed while the callback code is accessing its internal members.

Note that 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.

Elad Maimoni
  • 3,703
  • 3
  • 20
  • 37
  • In this specific example, I don't understand what is the added value of using enable_shared_from_this here, since the Client clears the timer callback in its destructor ? – Scylardor Sep 05 '20 at 01:42
  • 1
    @Scylardor imagine that during the time the timer callback is running, the desturctor is invoked in the main thread. The callback may be access destroyed 'this'. The actual clearing of the callback is neither atomic nor in sync with the timer. – Elad Maimoni Sep 05 '20 at 08:02
  • 1
    Oh ok, thank you for clearing that up. I forgot the multithreading implications of this. It makes sense now. Great example ! – Scylardor Sep 08 '20 at 02:15
  • 1
    I would vote this as the best answer. It clearly addresses the question: WHY is enable_shared_from_this useful? Other answers only try to explain what enable_shared_from_this does. – cyb70289 Mar 17 '21 at 04:57
  • 1
    @cyb70289 note that I just fixed a small mistake. Glad it helped. – Elad Maimoni Mar 17 '21 at 14:31
  • In this example,is it really useful to capture the client’s weak_ptr? Weak_ptr will not increase the reference count. I think the client’s destructor will still be called before the scope disappears. – clay Dec 19 '21 at 11:02
  • @clay you only need to extend the lifetime of the client object while the callback is accessing its internal fields. If you capture a shared_ptr, you will extend the client lifetime for the duration of the callback subscription. Also, you will create a cyclic reference which shared_ptr cannot handle. – Elad Maimoni Feb 20 '22 at 20:27
4

Note that using a boost::intrusive_ptr does not suffer from this problem. This is often a more convenient way to get around this issue.

blais
  • 687
  • 7
  • 9
  • Yes, but `enable_shared_from_this` allows you to work with an API which specifically accepts `shared_ptr<>`. In my opinion, such an API is usually *Doing It Wrong* (as it's better to let something higher in the stack own the memory) but if you're forced to work with such an API, this is a good option. – cdunn2001 Jun 05 '13 at 14:55
  • 2
    Better to stay within the standard as much as you can. – Sergei Feb 27 '19 at 00:15
4

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;
};           
mchiasson
  • 2,452
  • 25
  • 27
  • This will only work if these objects are always managed by a `shared_ptr`. You might want to change the interface to make sure it's the case. – curiousguy Nov 28 '17 at 23:47
  • 1
    You are absolutely correct @curiousguy. This goes without saying. I also like typedef-ing all of my shared_ptr to improve readability when defining my public APIs. For example, instead of `std::shared_ptr getParent const()`, I would normally expose it as `NodePtr getParent const()` instead. If you absolutely need access to the internal raw pointer (best example: dealing with a C library), there's `std::shared_ptr::get` for that, which I hate mentioning because I've this raw pointer accessor used too many times for the wrong reason. – mchiasson Dec 10 '17 at 15:49