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
@重复数据消除器-对。大脑放屁,我脑子里还有一段代码。这主意不错。