具有智能指针附加继承对象的深拷贝
deep copy with smart pointer-attached inherited objects
我不确定使用包含继承对象的智能指针对对象进行深层复制的最佳/最干净的解决方案是什么。归结起来,给定以下代码
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
包含类型 D1
和 D2
的对象,制作 v
的深层副本的最短/最优雅的方法是什么?我可以想到两种方法,但都有一些缺点:
- 在基础 class 中创建一个虚拟
A* clone()
方法,并在每个继承的 class 中重载它(如 here 所述)。缺点:clone方法需要在每个继承class中实例化并且可能有多个
- 为
V
创建一个副本 constructor/assignment 运算符。使用 dynamic_cast<D1/D2>
,检查附加了哪种类型的继承对象,并为该特定类型制作一个副本。缺点:需要在 V
. 的复制构造函数中遍历所有继承的 classes
每次将 class 添加到 A
下的层次结构时,选项 1 不需要您修改 V
。此外,如果添加的 class 没有实现 clone
,您将得到一个漂亮的闪亮编译器错误,而不是像选项 2 那样在 运行 时构建和失败的所有内容。
所以选项 1 更好。但你是对的,它有点重复。您必须为许多不同的类型编写类似的代码。幸运的是,C++ 有一个机制来处理这个问题:模板。
使用 CRTP class 我们可以自动实现 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;
};
上面使用的是原始指针,可能会通过返回 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。
好吧,让我们看看:
A
没有虚拟 dtor,因此 V
的 dtor 在 member-unique_ptr
试图以多态方式销毁 ist pointee 时调用 UB。
dynamic_cast
只能用于检查最派生的类型,如果它是有效的 final
,并且源类型有虚方法 and/or 基.虽然您似乎并非源自 D1
and/or D2
,但没有什么能阻止其他人这样做。而且您没有任何虚拟基础或方法。
至少改用 typeid
并添加一个虚拟 dtor。
使用虚拟 .clone()
允许您省略所有繁琐且容易出错的类型检查,并且在扩展到新的 class 时包含任何必要的更改。另一种方法是用地图注册它,在旁边存储指向克隆方法的指针,或者将其全部写成代码。
我不确定使用包含继承对象的智能指针对对象进行深层复制的最佳/最干净的解决方案是什么。归结起来,给定以下代码
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
包含类型 D1
和 D2
的对象,制作 v
的深层副本的最短/最优雅的方法是什么?我可以想到两种方法,但都有一些缺点:
- 在基础 class 中创建一个虚拟
A* clone()
方法,并在每个继承的 class 中重载它(如 here 所述)。缺点:clone方法需要在每个继承class中实例化并且可能有多个 - 为
V
创建一个副本 constructor/assignment 运算符。使用dynamic_cast<D1/D2>
,检查附加了哪种类型的继承对象,并为该特定类型制作一个副本。缺点:需要在V
. 的复制构造函数中遍历所有继承的 classes
每次将 class 添加到 A
下的层次结构时,选项 1 不需要您修改 V
。此外,如果添加的 class 没有实现 clone
,您将得到一个漂亮的闪亮编译器错误,而不是像选项 2 那样在 运行 时构建和失败的所有内容。
所以选项 1 更好。但你是对的,它有点重复。您必须为许多不同的类型编写类似的代码。幸运的是,C++ 有一个机制来处理这个问题:模板。
使用 CRTP class 我们可以自动实现 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;
};
上面使用的是原始指针,可能会通过返回 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。
好吧,让我们看看:
A
没有虚拟 dtor,因此V
的 dtor 在 member-unique_ptr
试图以多态方式销毁 ist pointee 时调用 UB。dynamic_cast
只能用于检查最派生的类型,如果它是有效的final
,并且源类型有虚方法 and/or 基.虽然您似乎并非源自D1
and/orD2
,但没有什么能阻止其他人这样做。而且您没有任何虚拟基础或方法。 至少改用typeid
并添加一个虚拟 dtor。使用虚拟
.clone()
允许您省略所有繁琐且容易出错的类型检查,并且在扩展到新的 class 时包含任何必要的更改。另一种方法是用地图注册它,在旁边存储指向克隆方法的指针,或者将其全部写成代码。