Search code examples
c++memory-managementc++17smart-pointersunique-ptr

How to deal uniformly with a factory returning unique_ptr and a getter returning a reference to global object?


Assume there is a global Foo object,

struct Foo {
    int i;
    ~Foo() { std::cout << "gone: " << i << std::endl; }
};

Foo globalFoo{7};

that it can be retrieved via a function that returns it by reference,

Foo& getFoo() {
    return globalFoo;
}

and that there's also a factory function to make a different Foo out of thin air,

std::unique_ptr<Foo> makeFoo() {
    return std::make_unique<Foo>(5);
}

The above is unchangeable.

In client code, whether one should use makeFoo or getFoo is determined by a runtime bool.

int main()
{
    bool b{false};

    std::cin >> b;

    /* type */ foo = b
                   ? /* via getFoo */
                   : /* via makeFoo */;
} 

What is the appropriate way to deal with this scenario?

I can tell that this is not the way:

    auto& foo = b
              ? getFoo()
              : *makeFoo();

because foo is a dangling reference right after its created, as the temporary result of makeFoo(), the unique_ptr will be destoryed, thus taking the managed Foo with it.

This too is not the way:

    auto& foo = b
              ? getFoo()
              : *makeFoo().release();

because the object *makeFoo().release() is leaked, unless I manually delete it.


Solution

  • You can wrap the result in shared_ptr with a deleter that does nothing in case this is a global object. Unlike for unique_ptr, deleter of shared_ptr is not a part of the type of the smart pointer itself, so the type will be the same for both cases.

    Something like this:

    auto foo = b ? std::shared_ptr(&/* via getFoo */, [](Foo*){})
                 : std::shared_ptr(/* via makeFoo */);