Как добавить значения в список <shared_ptr <Abstract>> - PullRequest
2 голосов
/ 27 апреля 2019

Мне нужно добавить элементы производных классов в список общих указателей на абстрактный класс.

Я пробовал это.Я знаю, что я пытаюсь создать экземпляр абстрактного класса в приведенном ниже примере, но я не знаю, как заставить его работать.

Упрощенные классы выглядят так:


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

проблема в следующем классе:

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

Я хочу избежать RTII и dynamic_cast.Я открыт для разных решений.Мне просто нужно как-то хранить элементы разных типов в одном контейнере.

Мне нужно использовать класс Data, например:

    Data test("Random Name");

    test.AddData(A(1,2)) 
        .AddData(B(3))
        .AddData(B(4));

Ответы [ 2 ]

2 голосов
/ 27 апреля 2019

В чем проблема?

Проблема в том, что make_shared<X> создаст общий объект, вызвав конструктор X. В вашем случае это невозможно, так как это абстрактный тип. Так что это даже не скомпилируется.

Если X не будет абстрактным, он скомпилируется и будет работать. Но это приведет к созданию нарезанного общего объекта.

Как это решить?

Вам необходимо использовать функцию виртуального клонирования в духе шаблона проектирования прототипа :

class Abstract
{
  public:
    virtual string toString() const = 0;
    virtual shared_ptr<Abstract> clone() const= 0; 
};

Вам придется переопределить его в производных классах, например:

class B : public Abstract
{
  public:
    ...
    shared_ptr<Abstract> clone() const override { return make_shared<B>(*this); } 
  ...
};

Затем вы можете заполнить список, используя преимущества полиморфизма:

Data& AddData ( const Abstract & a )   // return preferably a reference
{
    //Need to fix next line
    l1.push_back(a.clone());
    return (*this);   
}

Демоверсия

Дополнительное замечание

Если вы адепт метода цепочки , я думаю, вы бы хотели AddData() вернуть ссылку. Если нет, то каждый раз, когда вы вызываете AddData(), вы создаете копию Data, которая создает огромное количество ненужных копий.

0 голосов
/ 27 апреля 2019

Вы не можете поместить экземпляр абстрактного класса в список.Я бы либо перегружен функцией AddData (), либо использовал шаблоны.

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

или

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