Why create an object with a static shared_ptr returning member?

Viewed 311

I've seen such pattern around and am curious what's the benefit of such pattern. What's the difference between creating an object with static and with pure constructor?

class Foo {
static std::shared_ptr<Foo> create(); // why expose this function?

Foo(folly::observer::Observe<Config> config);
};
1 Answers

One reason to do this would be to force all instances of the object to be owned by shared_ptr's (instead of statically constructed). This is especially helpful when using shared_from_this().

For example, consider the following program:

#include <memory>

class Foo;

void globalFunc(const std::shared_ptr<Foo> &) {
    // do something with the ptr
}

class Foo
    : public std::enable_shared_from_this<Foo>
{
public:
    Foo() {}
    void classMemberFunc()
    {
        globalFunc(shared_from_this());
    }
};

In this program, a Foo object can access/pass a shared pointer to itself, similar to how it can access/pass the this pointer. When classMemberFunc() is called on an object of Foo, globalFunc receives a reference to a shared_ptr the holds Foo.

However, with this design, Foo needs to be owned by a shared_ptr in the first place.

int main()
{
    // valid use
    auto sptr = std::make_shared<Foo>();
    sptr->classMemberFunc();
}

If a Foo object isn't owned by a shared_ptr, shared_from_this() has undefined behavior before C++17 and a runtime error in C++17.

int main()
{
    // invalid use - undefined behavior or runtime error
    Foo nonPtrFoo;
    nonPtrFoo.classMemberFunc();
}

We would like to prevent this at compile time. We can do this using the static "create" method and a private constructor.

class Foo
    : public std::enable_shared_from_this<Foo>
{
public:
    static std::shared_ptr<Foo> create() // force shared_ptr use
    {
        return std::shared_ptr<Foo>(new Foo);
    }
    void classMemberFunc()
    {
        globalFunc(shared_from_this());
    }
private:
    Foo() {} // prevent direct construction
};

int main()
{
    // valid use
    auto sptr = Foo::create();
    sptr->classMemberFunc();

    // invalid use - now compile error
    Foo nonPtrFoo;
    nonPtrFoo.classMemberFunc();
}
Related