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

Time:10-07

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?

CodePudding user response:

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

CodePudding user response:

You could do this:

auto h = std::make_unique<Helper>();
auto p = condition ? std::unique_ptr<Base>(new Derived(*h)) : std::move(h);

But the std::optional option you provided looks simpler and without memory allocation.

CodePudding user response:

I would refactor the code:

Helper h;

if (condition)
{
    Derived d{h};

    the_rest_of_code(d);
}
else
{
    the_rest_of_code(h);
}

// ...

void the_rest_of_code(Helper &h)
{
  // ... whatever happens next.
}

Possible variations include using a lambda, to keep things confined.

  • Related