C++ 智能指针连接继承对象的深度复制

C++ 智能指针连接继承对象的深度复制,c++,c++11,polymorphism,smart-pointers,deep-copy,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; };

我不确定使用包含继承对象的智能指针创建对象的深度副本的最佳/最干净的解决方案是什么。归结起来,给出以下代码

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
}
A类{};
D1类:公共A{
公众:
int-x1=0;
};
D2类:公共A{
公众:
int x2=2;
};
第五类{
公众:
V(A*A):ptr(A){}
缺点:克隆方法需要在每个继承的类中实例化,并且可能有多个
  • V
    创建复制构造函数/赋值运算符。使用
    dynamic\u cast
    ,检查附加了哪种类型的继承对象,并为该特定类型创建一个副本。缺点:需要遍历
    V
    的复制构造函数中的所有继承类

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

    所以选项1更好,但是你是正确的,它有点重复。你必须为许多不同类型编写相似的代码。幸运的是,C++有一个机制来处理:模板。 使用类,我们可以自动实现

    clone
    功能。所有
    D1
    D2
    需要做的是从中间人继承,而不是直接从
    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;
    };
    

    您可以实时查看它。

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

    所以选项1更好,但是你是正确的,它有点重复。你必须为许多不同类型编写相似的代码。幸运的是,C++有一个机制来处理:模板。 使用类,我们可以自动实现

    clone
    功能。所有
    D1
    D2
    需要做的是从中间人继承,而不是直接从
    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;
    };
    

    你可以现场观看。

    好吧,让我们来看看:

  • A
    没有虚拟dtor,因此当成员-
    unique\u ptr
    尝试以多态方式销毁ist指针对象时,
    V
    的dtor调用UB

  • dynamic\u cast
    只能用于检查最派生的类型,如果它是有效的
    final
    ,并且源类型具有虚拟方法和/或基础。虽然您似乎不是从
    D1
    和/或
    D2
    派生,但没有任何东西可以阻止其他人这样做。而且您没有任何虚拟基础或met霍兹。 至少改用
    typeid
    并添加虚拟dtor

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


  • 那么我们来看看,

  • A
    没有虚拟dtor,因此当成员-
    unique\u ptr
    尝试以多态方式销毁ist指针对象时,
    V
    的dtor调用UB

  • dynamic\u cast
    只能用于检查最派生的类型,如果它是有效的
    final
    ,并且源类型具有虚拟方法和/或基础。虽然您似乎不是从
    D1
    和/或
    D2
    派生,但没有任何东西可以阻止其他人这样做。而且您没有任何虚拟基础或met霍兹。 至少改用
    typeid
    并添加虚拟dtor

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


  • 我喜欢解决方案1。解决方案2很糟糕。我看不到更好的解决方案。我不久前一直在试验深度可复制
    std::unique_ptr
    包装器,它可能对您有用:我喜欢解决方案1。解决方案2很糟糕。我看不到更好的解决方案。我一直在试验深度可复制
    std::unique_ptr
    wr不久前,它可能对您有用:为了获得最佳效果,请将
    assert(typeid(C)=typeid(*this));
    添加到
    AClone::clone()
    @Deduplicator-这将始终是错误的。
    typeid(*this)
    AClone
    @Deduplicator-对。大脑放屁,我脑子里还有另一段代码。这不是一个坏主意。为了获得最佳效果,请添加
    assert(typeid(C)=typeid(*this));
    AClone::clone()
    @Deduplicator-这总是错误的。
    typeid(*this)
    AClone
    @重复数据消除器-对。大脑放屁,我脑子里还有一段代码。这主意不错。