Search code examples
c++inheritancememory-managementmemory-leakspolymorphism

Dynanically choose derived class to instantiate – how to take care of memory?


Consider the following setup:

struct Base {
    virtual void f() = 0;
    virtual ~Base() {};
};
struct Helper: Base {
    virtual void f() {}
};
struct Derived: public Base {
    Derived(Helper& helper): m_helper(helper) {}
    void f() {}
    Helper& m_helper;
};

that I, at the moment, use conditionally using preprocessor:

Helper h;
#if condition
    Derived d(h);
#else
    Helper& d(h);
#endif

If you wish, Derived is a class that enhances Helper.

Now, how can I do that dynamically, at best as little error-prone as possible?

std::unique_ptr<Base> p = new Helper();
if(condition){
    p = new Derived(*helper);
}

won't work, I guess, because on the assignment p = new Derived, I will cause deletion of the original Helper, won't I?

Of course, I can simply do something like

Helper h;
std::optional<Derived> d = condition ? Derived(h) : {};
Base& p = d ? d : h;

Or, go fully manually, and do

Helper h;
Derived* d = condition ? new Derived(h) : 0;
Base& p = d ? *d : h;
...
if(d) delete d;

None of these solutions really pleases me. What is the advisable way?


Solution

  • You can use std::shared_ptr instead of std:unique_ptr:

    auto h = std::make_shared<Helper>();
    std::shared_ptr<Base> p;
    if (condition) {
        p = std::make_shared<Derived>(*h);
    } else {
        p = h;
    }
    

    Alternatively:

    auto h = std::make_shared<Helper>();
    auto p = condition ? std::shared_ptr<Base>(new Derived(*h)) : h;
    

    Though, I would suggest sticking with the std::optional approach you proposed, as it doesn't rely on dynamic allocation. However, what you showed won't compile, but this does:

    Helper h;
    std::optional<Derived> d;
    if (condition) d.emplace(h);
    Base& p = d ? *d : static_cast<Base&>(h);
    

    Online Demo