【问题标题】:deep copy with smart pointer-attached inherited objects带有智能指针附加的继承对象的深拷贝
【发布时间】:2019-03-03 06:16:00
【问题描述】:

我不确定使用包含继承对象的智能指针制作对象的深层副本的最佳/最干净的解决方案是什么。归结为以下代码

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&lt;D1/D2&gt;,检查附加了哪种继承对象并为该特定类型制作副本。缺点:需要遍历V的复制构造函数中的所有继承类。

【问题讨论】:

  • 我喜欢解决方案 1。解决方案 2 太糟糕了。我没有看到更好的解决方案。
  • 前段时间我一直在尝试使用可深度复制的std::unique_ptr 包装器,它可能对你有用:codereview.stackexchange.com/questions/197579/…

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


【解决方案1】:

选项 1 不会要求您在每次将类添加到 A 下的层次结构时修改 V。此外,如果添加的类没有实现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

【讨论】:

  • 为获得最佳效果,请将assert(typeid(C) == typeid(*this)); 添加到AClone::clone()
  • @Deduplicator - 那总是错误的。 typeid(*this)AClone&lt;C&gt;
  • @Deduplicator - 对。脑子放个屁,我脑子里又多了一段代码。这不是一个坏主意。
【解决方案2】:

好吧,我们来看看:

  1. A 没有虚拟 dtor,所以V 的 dtor 会在成员-unique_ptr 尝试多态销毁 ist 指针时调用 UB。

  2. dynamic_cast 只能用于检查派生最多的类型,前提是它实际上是 final,并且源类型具有虚方法和/或基。虽然您似乎不是来自D1 和/或D2,但没有什么能阻止其他人这样做。而且您没有任何虚拟基础或方法。 至少改用typeid 并添加一个虚拟dtor。

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

【讨论】:

    猜你喜欢
    • 2014-07-06
    • 1970-01-01
    • 2010-12-28
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 2019-03-01
    • 2015-07-21
    相关资源
    最近更新 更多