How to add values to list<shared_ptr<Abstract>>

岁酱吖の 提交于 2019-12-11 02:25:18

问题


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));

回答1:


What's the problem ?

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.

How to solve it ?

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);   
}

Online demo

Additional remark

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.




回答2:


You can't push an abstract class instance to a list. I'd either overloaded AddData() function or used templates.

class Data
{
  public:
    Data(const string & name) : name (name) {}
    Data& AddData (const A &a)
    {
        l1.push_back(make_shared<A>(a));
        return (*this);   
    }

    Data& AddData (const B &b)
    {
        l1.push_back(make_shared<B>(b));
        return (*this);   
    }
  private:
    list<shared_ptr<Abstract>> l1;
    string name;
};

or

class Data
{
  public:
    Data(const string & name) : name (name) {}

    template<typename T>
    Data& AddData (const T &a)
    {
       l1.push_back(make_shared<T>(a));
       return (*this);   
    }

  private:
    list<shared_ptr<Abstract>> l1;
    string name;
};


来源:https://stackoverflow.com/questions/55879042/how-to-add-values-to-listshared-ptrabstract

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!