我有一个抽象的基类,有几个具体的派生类;这些类都不管理任何资源。
#include <memory>
#include <vector>
// this is a pure abstract class that contains no resources
class Base {
public:
Base() {};
virtual int doSomething() = 0;
};
class Derived : public Base {
public:
Derived() {};
// this mutates the derived class
int doSomething() override { return 0; };
};
class Derived2 : public Base {
public:
Derived2() {};
// this mutates the derived class
int doSomething() override { return 0; };
};
我有一个函数返回一个随机派生的实例(Derived1,Derived2,Derived3,取决于随机数抛出)。
std::unique_ptr<Base> randomDerivedInstance() {
// pick a random number here and return Derived1 or Derived2 etc.
// for the purpose of this problem, I'm just returning a fixed derived class
return std::make_unique<Derived>();
}
我有一个结构,我想将此派生实例存储在
中struct DataStruct {
// this can contain Derived1 or Derived2
std::unique_ptr<Base> base;
// other things in struct omitted for clarity
// obviously this won't work
DataStruct(std::unique_ptr<Base> base) : base(base) {};
};
我从随机函数返回一个唯一的指针,并希望将一个副本保存到结构中,然后在其上调用doSomething
,对类内部执行多个变异操作,我不想要它们影响存储在列表中的副本。
如果我知道派生实例的类型,我会使用复制构造函数来创建一个新实例并将其添加到向量中,但在这种情况下我不知道我正在尝试的实例的具体类型添加,所以我不知道使用哪个具体的构造函数。
int main() {
// I want to create a vector of random instances
std::vector<DataStruct> list;
// I create a random instance
auto myDerived = randomDerivedInstance();
// and I want to push a copy of myDerived before I do something with it
// obviously this doesn't work because its a unique_ptr
// what can I do here?
list.push_back(DataStruct(myDerived));
// do something that mutates myDerived
myDerived->doSomething();
// I don't want my mutations to myDerived to affect the list copy
}
由于我试图在unique_ptr
构造函数中分配DataStruct
,上面的代码由于显而易见的原因而无法编译。
我需要对此架构和代码进行哪些更改才能使其按预期工作?即将随机派生实例的值副本添加到结构中,以便我可以改变原始实例(反之亦然,添加原始实例和变异副本)。
提前感谢您的帮助!
答案 0 :(得分:2)
在课程Base
中添加虚拟成员函数clone
:
virtual auto clone() const
-> std::unique_ptr<Base>
= 0;
在每个派生类Derived
中重写以提供派生类特定克隆:
auto clone() const
-> std::unique_ptr<Base>
override
{ return std::unique_ptr<Base>( new Derived{ *this } ); }
可以以更高级的方式执行此操作,如果您在编译时知道派生类最多的类,则可以静态获取该类型的克隆,但似乎不需要它。
免责声明:袖珍代码,未经编译器审核。
很久以前,clone
函数被称为虚拟构造函数,该术语在the FAQ item about this中使用。我认为它是由Coplien介绍的。目前的常见问题解答文本没有说明。
另外值得注意的是:在C ++ 11及更高版本中clone
函数实现的生成可以部分自动化Derived
继承自实现的实现,而实现继承自Base
,转发构造函数参数。
C ++ 03不支持转发,因此必须使用代码生成宏(恶意但实际上是当时唯一真正的解决方案)等方案,通过虚拟继承层次结构中的支配实现继承(极其复杂) (或丑陋),或者像我们现在在C ++ 11及更高版本中所做的那样,但是使用自己动手的参数转发方案(有些任意限制)。
有关这些旧C ++ 03技术的概述,请参阅我的2010年博客文章“3 ways to mix in a generic cloning implementation”。