I need to add elements of derived classes into list of shared pointers to abstract class.
I've been trying this. I know that I am trying to create instance of abstract class in the example below, but I have no idea how to make it work.
Simplified classes look like this:
using namespace std;
class Abstract
{
public:
virtual string toString() const = 0;
};
class A : public Abstract
{
public:
A(int a, int b) : a(a), b(b)
{}
string toString() const { return "A"; }
private:
int a;
int b;
};
class B : public Abstract
{
public:
B(int b) : b(b)
{}
string toString() const { return "B"; }
private:
int b;
};
The problem is in the following class:
class Data
{
public:
Data(const string & name) : name (name)
{}
Data AddData ( const Abstract & a )
{
//Need to fix next line
l1.push_back(make_shared<Abstract>(a));
return (*this);
}
private:
list<shared_ptr<Abstract>> l1;
string name;
};
I want to avoid RTII and dynamic_cast. I am open to different solutions. I just need to somehow store elements of different types into single container.
I need to use class Data like this:
Data test("Random Name");
test.AddData(A(1,2))
.AddData(B(3))
.AddData(B(4));
The problem is that make_shared<X>
will create a shared object by invoking the constructor of X
. In your case this is not possible, since it's an abstract type. So it won't even compile.
If X
would not be abstract, it would compile and appear to work. But it would result in creating a sliced shared object.
You need to use a virtual cloning function in the spirit of the prototype design pattern:
class Abstract
{
public:
virtual string toString() const = 0;
virtual shared_ptr<Abstract> clone() const= 0;
};
You'll have to override it in the derived classes, for example:
class B : public Abstract
{
public:
...
shared_ptr<Abstract> clone() const override { return make_shared<B>(*this); }
...
};
You may then populate the list taking advantage of the polymorphism:
Data& AddData ( const Abstract & a ) // return preferably a reference
{
//Need to fix next line
l1.push_back(a.clone());
return (*this);
}
If you're an adept of method chaining, I think you'd want AddData()
to return a reference. If not, then each time you'd invoke AddData()
, you would create a copy of Data
, which would create an awful lot of unnecessary copies.