Lets say I have an abstract class that is expensive to create and copy:
class AbstractBase {
public:
AbstractBase() {
for (int i = 0; i < 50000000; ++i) {
values.push_back(i);
}
}
virtual void doThing() = 0;
private:
vector<int> values;
};
It has two subclasses FirstDerived
:
class FirstDerived : public AbstractBase {
public:
void doThing() {
std::cout << "I did the thing in FirstDerived!\n";
}
};
and SecondDerived
:
class SecondDerived : public AbstractBase {
public:
void doThing() {
std::cout << "I did the thing in SecondDerived!\n";
}
};
Further, I would like to make a class that utilizes FirstDerived
or SecondDerived
using composition (not aggregation). Meaning that I want ComposedOfAbstractBase
to own whichever temporary is passed in. If I weren't using abstract classes in this class would look like: (in C++11)
class ComposedOfWhicheverDerived {
public:
ComposedOfWhicheverDerived(AbstractBase abstract_base) : abstract_base(std::move(abstract_base)) {;}
private:
AbstractBase abstract_base;
};
However, this does not work with abstract classes because I cannot ever create an instance of AbstractBase
, even if I am careful about not passing in a temporary AbstractBase
, like so:
ComposedOfWhicheverDerived a(FirstDerived());
To the compiler this is just as bad as:
ComposedOfWhicheverDerived b(AbstractBase());
Because I still have an instance of AbstractBase
in the class declaration.
The next solution I came up with is:
class ComposedOfAbstractBase {
public:
ComposedOfAbstractBase(AbstractBase&& abstract_base) : some_derived_instance(abstract_base) {;}
private:
AbstractBase& some_derived_instance;
};
This works perfectly (even though I don't fully understand it)! Both of these instances are valid and work as intended:
ComposedOfAbstractBase a(FirstDerived());
ComposedOfAbstractBase b(SecondDerived());
It doesn't create a copy of whatever AbstractBase
temporary is passed in, and storing a reference to an AbstractBase
is allowed. Though at best the reference to an rvalue reference seems unclear: it does not convey that ComposedOfAbstractBase
owns whichever temporary is passed in. In addition to that, it turns out that this solution seems to be sub-optimal. To show this I created this class:
class ComposedOfFirstDerived {
public:
ComposedOfFirstDerived(FirstDerived first_derived) : first_derived(std::move(first_derived)) {;}
private:
FirstDerived first_derived;
};
Which can only take in a FirstDerived
, so we can apply the std::move
to offload ownership of the temporary. I can make an instance like so:
ComposedOfFirstDerived c(FirstDerived());
Interestingly enough, this class consistently is 10% faster to create than ComposedOfAbstractClass
.
Does anybody know what is going on here? Why is ComposedOfFirstDerived
so much faster to create than ComposedOfAbstractBase
? Is there a better way to do composition with abstract classes or am I stuck with a sub-optimal solution?
Sorry if this was a mouthful of a question. I appreciate anyone who takes the time to read through it and give a genuine answer, because I am beyond stumped!
ComposedOfAbstractBase
is not a solution. You're holding a dangling reference.Since
AbstractBase
is, as the name suggests, abstract - you cannot hold one by value. You can only hold one by reference or by pointer. Since a reference cannot own the object, that leaves you with the pointer. And the modern way of owning a pointer is to useunique_ptr
:Note that your
AbstractBase
does not have a virtual destructor. You should fix that.