std::shared_ptr and shared_from_this

When introduced to the std::shared_ptr, many people immediately want to produce a function that returns a std::shared_ptr to this.

//BAD! creates multiple control blocks to the same pointer std::shared_ptr<Thingy> get_a_thingy() { return this; }

When the function above is used to generate a std::shared_ptr, each call will create a new control block. The first std::shared_ptr to go out of scope will destruct the object, leaving the other std::shared_ptr objects pointing to the deleted memory.

The C++ standard gets around this issue via the function shared_from_this, which safely creates shared pointers to this without duplicate control blocks.

In order to enable the shared_from_this function, your class must inherit from std::enable_shared_from_this<T>:

class Thingy : public std::enable_shared_from_this<Thingy> {...}

Using the shared_from_this function, the correct example for returning a std::shared_ptr to our object would be:

//Requires a shared_ptr to be created before use std::shared_ptr<Thingy> get_a_thingy() { return shared_from_this(); }

One caveat to the shared_from_this function: a std::shared_ptr object must be created before shared_from_this is used. The shared_from_this function searches for the existing control block rather than creating a new one.

The simplest way to control this is to make the constructor private. Objects will be created using a static factory function that passes arguments to the private constructor.

// factory function that perfect-forwards args // to a private ctor template<typename ... T> static std::shared_ptr<Thingy> create(T&& ... t) { return std::shared_ptr<Thingy>(new Thingy(std::forward<T>(t)...)); }

Using std::forward in the factory allows you to have multiple constructor prototypes while using a single create function:

private: //We make constructor private - shared_from_this() breaks if a shared //ptr has never been created before. (It looks for the control block) Thingy() : name_("Nameless Thing") {} Thingy(std::string name) : name_(name) {}

Now, the C++ core guidelines says we should use make_shared instead of new, so let’s try that:

// factory function that perfect-forwards args // to a private ctor - we use this to create objects template<typename... T> static std::shared_ptr<SharedThing> create(T&&... t) { return std::make_shared<SharedThing>(std::forward<T>(t)...); //return std::shared_ptr<SharedThing>(new SharedThing(std::forward<T>(t)...)); }

Well, we get an error:

include/c++/v1/memory:4324:5: error: static_assert failed due to requirement 'is_constructible<SharedThing, char const (&)[8]>::value' "Can't construct object in make_shared" static_assert( is_constructible<_Tp, _Args...>::value, "Can't construct object in make_shared" );

and:

In file included from ../examples/cpp/shared_ptr.cpp:1: include/c++/v1/memory:2155:9: error: field of type 'SharedThing' has private constructor : __value_(_VSTD::forward<_Args>(_VSTD::get<_Indexes>(__args))...) {}

What’s going on here? Looking at cppreference.com, we can find a note for make_shared:

std::shared_ptr(new T(args...)) may call a non-public constructor of T if executed in context where it is accessible, while std::make_shared requires public access to the selected constructor.”

So our private constructor is a problem, but I don’t really want to make the thing public. As a workaround, we can define another class derived from SharedThing which simply constructs a SharedThing, and use that with make_shared:

// factory function that perfect-forwards args // to a private ctor - we use this to create objects template<typename... T> static std::shared_ptr<SharedThing> create(T&&... t) { // C++ core guidelines say we shouldn't do this: //return std::shared_ptr<SharedThing>(new SharedThing(std::forward<T>(t)...)); // But this doesn't work because our constructor is private, and we want that // for use with enable_shared_from_this //return std::make_shared<SharedThing>(std::forward<T>(t)...); // So we'll make a "fake" derived class that constructs our shared thing // using a public constructor... struct EnableMakeShared : public SharedThing { EnableMakeShared(T&&... arg) : SharedThing(std::forward<T>(arg)...) {} }; // and use that type with make_shared return std::make_shared<EnableMakeShared>(std::forward<T>(t)...); }

So now that we’ve set up our class to enable shared_from_this, how do we actually put everything together?

When creating the object, simply use the static factory function we defined:

auto thingy = Thingy::create("Thing 1");

Once the object is created, you can safely generate a std::shared_ptr:

auto t = thingy->get_a_thingy();

And that’s all there is to it!

Example Source Code

Further Reading

Migrating from C to C++ Articles

Share Your Thoughts

This site uses Akismet to reduce spam. Learn how your comment data is processed.