我不确定使用包含继承对象的智能指针对对象进行深层复制的最佳/最干净的解决方案是什么 . 将它煮沸,给出以下代码
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
}
其中vector v
包含 D1
和 D2
类型的对象,是什么是制作 v
的深层副本的最短/最优雅的方法?我可以想到两种方法,都有一些缺点:
-
在基类中创建一个虚拟
A* clone()
方法,并在每个继承的类中重载它(如here所述) . 缺点:克隆方法需要在每个继承的类中实例化,并且可能有多个 . -
为
V
创建复制构造函数/赋值运算符 . 使用dynamic_cast<D1/D2>
,检查附加了哪种继承对象,并为该特定类型制作副本 . 缺点:需要遍历V
的复制构造函数中的所有继承类 .
2 回答
每次将类添加到
A
下的层次结构中时,选项1都不需要您修改V
. 此外,如果添加的类没有实现clone
,您将获得一个漂亮的闪亮编译器错误,而不是像运行时一样在运行时构建和失败 .因此选项1更好 . 但你是对的,它有点重复 . 您必须为许多不同类型编写类似的代码 . 幸运的是,C有一个处理它的机制:模板 .
使用CRTP类,我们可以自动实现
clone
函数 . 所有D1
和D2
需要做的是从中间人继承,而不是直接从A
继承:以上是使用原始指针,并且可能会通过返回
unique_ptr
来改进,但为了简洁起见,这个想法已经归结 .人们还可以为这个
clone
函数添加一些防御性编程 .你可以看到它,here .
好吧,让我们来看看它:
A
没有虚拟dtor,所以当成员unique_ptr
尝试多态破坏ist指针时,V
的dtor会调用UB .dynamic_cast
只能用于检查派生最多的类型(如果它实际上是final
),并且源类型具有虚拟方法和/或基础 . 虽然您似乎不是从D1
和/或D2
派生的,但's nothing stopping anyone else from doing so. And you don' t有任何虚拟基础或方法 . 至少使用typeid
并添加虚拟dtor .使用virtual
.clone()
允许您省略所有繁琐且容易出错的类型检查,并在扩展到新类时包含任何必要的更改 . 另一种方法是使用映射进行注册,将指针存储在克隆方法旁边,或者将其全部写为代码 .