使用智能指针附加的继承对象的深层复制
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
的深层副本的最短/最优雅的方法是什么?我可以想到两种方法,都有一些缺点:
- 在基类中创建虚拟
A* clone()
方法,并在每个继承的类中重载该方法(如此处所述(。缺点:克隆方法需要在每个继承的类中实例化,并且可能有多个。 - 为
V
创建复制构造函数/赋值运算符。使用dynamic_cast<D1/D2>
,检查附加了哪种继承对象,并为该特定类型创建副本。缺点:需要在V
的复制构造函数中遍历所有继承的类。
选项 1 不需要每次将类添加到A
下的层次结构时修改V
。此外,如果添加的类没有实现clone
则会收到一个漂亮的编译器错误,而不是像选项 2 中那样在运行时构建和失败的所有内容。
所以选项 1 更好。但你是对的,这有点重复。您必须为许多不同的类型编写类似的代码。幸运的是,C++有一个机制来处理这个问题:模板。
使用 CRTP 类,我们可以自动实现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
你可以在这里看到它。
好吧,让我们看看它:
-
A
没有虚拟的 dtor,所以当成员unique_ptr
试图多态地破坏 ist pointee 时,V
的 dtor 会调用 UB。 -
dynamic_cast
只能用于检查最派生的类型,如果它被有效地final
,并且源类型具有虚拟方法和/或基。虽然你似乎不是从D1
和/或D2
中衍生出来的,但没有什么能阻止其他人这样做。而且你没有任何虚拟的基础或方法。 至少改用typeid
并添加一个虚拟 dtor。 -
使用虚拟
.clone()
可以省略所有繁琐且容易出错的类型检查,并在扩展到新类时包含任何必要的更改。另一种方法是将其注册到映射中,在旁边存储指向克隆方法的指针,或者将其全部写为代码。
- C++:返回一个基于范围 for 循环迭代器,其中包含继承对象
- 使用智能指针附加的继承对象的深层复制
- 虚拟类继承对象大小问题
- 基对象和继承对象,用于将标准变量包装在C++中
- 为什么私有继承对象允许成员函数将派生的*转换为基*,而外部不能
- 是否有一种方法可以专门使用继承对象的一般方法
- 如何将接口对象强制转换为特定的继承对象
- MFC C++ 无法在动态创建的 CButton 继承对象上设置映像
- 在容器中存储多个继承对象
- QObject基类到继承对象的C++类型转换
- C++ 中的继承对象和属性丢失
- 继承对象列表 - C++
- 静态分配继承对象的数组
- 正确实例化双重继承对象
- 我是否需要使用继承对象(相对于基对象)覆盖我的虚函数?
- c++继承.对象调用超类方法而不是自己的方法
- 基对象和继承对象的向量
- 将多继承对象转换为void*或从void*转换为多继承对象
- 如何在c++中表示继承对象的树?
- 继承对象上指针算术的安全性