假设我们有以下类层次结构:
class Base {
...
};
class Derived1 : public Base {
...
};
class Derived2 : public Base {
...
};
鉴于Base*
可以指向Derived1
或Derived2
对象,如果具体类型未知,我如何制作实际对象的副本。我想过定义复制构造函数,但我不认为在不知道所涉及的实际类型的情况下这是可能的。我能想到的唯一解决方案是在层次结构中为每种类型定义clone()
方法。任何人都可以想到更优雅的东西吗?
答案 0 :(得分:13)
不幸的是,虚拟克隆/复制模式是您唯一真正的选择。
这有各种变化,但基本上它们都归结为为每种可以返回新副本的类型编写函数。
答案 1 :(得分:4)
您需要在层次结构中的每个对象上实现虚拟克隆方法。你的未定义类型的对象怎么会知道如何复制自己?
为了使编写派生类的人更加明显,你可能会考虑在基类中使clone方法抽象化,以迫使人们至少写一些东西。
在我的代码的几个地方,我还测试以确保正确实现to_string()方法以将对象序列化为字符串。我在我的班级中用来同时测试clone和to_string的具体测试如下:
Base *obj1, *obj2;
# initialize obj1 in a reasonable manner
obj2 = obj1->clone();
assert( obj1->to_string() == obj2->to_string() );
这是测试clone方法()和对象序列化到字符串(因此它不是严格来说是单元测试),但它是一个简单的环绕工厂中所有对象的循环以确保他们遵循一些实现clone()和to_string()的最低标准。
编辑:更新了代码并修正了strager的评论。谢谢!
答案 2 :(得分:2)
我认为这样做并不容易。实际上,在Effective C ++中,Meyers警告说,如果你有一个按值传递的函数,并说你有
void foo(Base b)
并且您将Derived1 d1
传递给foo
,副本将被删除 - 即不会复制派生的部分。
但是此刻我正在引用记忆......
答案 3 :(得分:0)
复制构造函数是不可能的(据我所知)因为在调用构造函数之前分配了内存。使用已知类型的复制构造函数,而不是对象的类型。
Base *foo = new Derived1();
Base bar = *foo; // Base's copy constructor is called.
您最好的选择是按照您的建议提供clone()
方法。
答案 4 :(得分:0)
如果您想要透明的克隆方法,请查看Implicit Sharing。
答案 5 :(得分:0)
你对clone()
的看法是正确的。您不能使用构造函数,因为它们强制静态绑定。您不能按值返回对象,因为这会再次强制静态绑定。因此,您需要一个返回动态分配对象的虚函数。
答案 6 :(得分:-1)
另一种方法是创建类似工厂的Base
,假设您知道所有派生类:
第二个想法,这可能是一个坏主意!
class Base {
public:
static Base *clone(const Base *obj) {
if((Derived1 *o = dynamic_cast<Derived1 *>(obj)) {
return new Derived1(*o);
} else if((Derived2 *o = dynamic_cast<Derived2 *>(obj)) {
return new Derived2(*o);
}
/* TODO When more classes are added, put them here. (Probably bad design, yes.) */
return new Base(obj);
}
Base *clone() const {
return clone(this);
}
};