使用智能指针附加的继承对象进行深度复制

时间:2018-09-27 10:43:22

标签: c++ c++11 polymorphism smart-pointers deep-copy

我不确定使用包含继承对象的智能指针对对象进行深层复制的最佳/最干净的解决方案是什么。给出下面的代码

class A {};

class D1 : public A{
public:
    int x1 = 0;
};

class D2 : public A {
public:
    int x2 = 2;
};


class V {
public:
    V(A* a) : ptr(a) {}
    std::unique_ptr<A> ptr;
};    

void run() {
    std::vector<V> v;
    v.push_back(V(new D1));
    v.push_back(V(new D2));
    /// I want to make a deep copy of v here
}

向量v包含类型D1D2的两个对象,制作v的深层副本的最短/最优雅的方法是什么?我可以想到两种方式,都有一些缺点:

  1. 在基类中创建一个虚拟的A* clone()方法,并在每个继承的类中重载它(如here所述)。缺点:clone方法需要在每个继承的类中实例化,并且可能有多个。
  2. V创建一个复制构造函数/赋值运算符。使用dynamic_cast<D1/D2>,检查连接了哪种继承对象,并为该特定类型创建一个副本。缺点:需要遍历V的副本构造函数中的所有继承的类。

2 个答案:

答案 0 :(得分:3)

选项1不需要每次将类添加到V下的层次结构时都需要修改A。此外,如果添加的类未实现clone,您将得到一个漂亮的编译器错误,而不是像选项2中那样在运行时构建和失败的所有内容。

所以选项1更好。但是您是正确的,它有些重复。您必须为许多不同的类型编写类似的代码。幸运的是,C ++有一种处理该问题的机制:模板。

使用CRTP类,我们可以自动实现clone函数。 D1D2所要做的就是从中间人继承,而不是直接从A继承:

class A {
public:
  virtual A* clone() const = 0;
  virtual ~A() = default;
};

template<class C>
struct AClone : A {
  A* clone() const override {
    return new C(*static_cast<C const*>(this));
  }
};

class D1 : public AClone<D1> {
public:
    int x1 = 0;
};

class D2 : public AClone<D2> {
public:
    int x2 = 2;
};

上面使用的是原始指针,可以通过返回unique_ptr来改善,但这是为简洁起见而提出的想法。

还可以在此clone函数中添加一些防御性编程。

static_assert(std::is_convertible<C*, A*>::value,"");
static_assert(std::is_convertible<C*, AClone*>::value,"");
// These two check `C` is derived unambiguasly from `A` via this specialization

assert(typeid(C) == typeid(*this));
// Check the most derived type is as expected, suggested by Deduplicator

here,您可以实时看到它。

答案 1 :(得分:1)

好吧,让我们来看一下:

  1. A没有虚拟dtor,因此当成员V试图多态破坏ist pointee时,unique_ptr的dtor调用UB。

  2. dynamic_cast仅可用于检查最有效的类型final,并且源类型具有虚拟方法和/或基数。尽管您似乎并非源自D1和/或D2,但没有什么能阻止其他人这样做。而且您没有任何虚拟基础或方法。 至少改为使用typeid并添加一个虚拟dtor。

  3. 使用虚拟.clone()可以省略所有繁琐且易于出错的类型检查,并且在扩展到新类时包含任何必要的更改。另一种选择是在地图上注册它,在旁边存储指向克隆方法的指针,或者将其全部写为代码。