【问题标题】:How to add values to list<shared_ptr<Abstract>>如何将值添加到列表<shared_ptr<Abstract>>
【发布时间】:2019-04-27 09:24:07
【问题描述】:

我需要将派生类的元素添加到指向抽象类的共享指针列表中。

我一直在尝试这个。我知道我正在尝试在下面的示例中创建抽象类的实例,但我不知道如何使它工作。

简化的类如下所示:


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 test("Random Name");

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

【问题讨论】:

  • 您不能创建抽象类的实例。如果要实例化 AB 对象,您需要以某种方式在 Data 中做出决定。

标签: c++ list polymorphism abstract-class shared-ptr


【解决方案1】:

有什么问题?

问题是make_shared&lt;X&gt;会通过调用X的构造函数来创建共享对象。在您的情况下,这是不可能的,因为它是一种抽象类型。所以它甚至不会编译。

如果X 不是抽象的,它会编译并看起来可以工作。但这会导致创建一个sliced 共享对象。

如何解决?

您需要本着prototype design pattern的精神使用虚拟克隆功能:

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

Online demo

补充说明

如果您精通method chaining,我想您会希望AddData() 返回参考。如果不是,那么每次调用AddData() 时,都会创建Data 的副本,这会创建大量不必要的副本。

【讨论】:

  • 谢谢。这正是我所需要的。
【解决方案2】:

您不能将抽象类实例推送到列表中。我要么重载 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;
};

【讨论】:

  • 虽然可以考虑这两种方法,但它们都依赖于编译时已知的对象类型。因此,它将适用于 OP 的示例,其中添加的对象的类型在编译时是已知的。但是,它在多态用例中会失败,例如,如果您尝试添加这样定义的引用 c:B b(5); Abstract &amp;c=b; (demo)
猜你喜欢
  • 2021-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多