5

假设我们有以下类层次结构:

class Base {
    ...
};

class Derived1 : public Base {
    ...
};

class Derived2 : public Base {
    ...
};

给定一个Base*可以指向aDerived1Derived2对象的a,鉴于它的具体类型未知,我如何制作实际对象的副本。我想过定义复制构造函数,但我认为如果不知道所涉及的实际类型,这是不可能的。我能想到的唯一解决方案是为层次结构中的每种类型定义一个clone()方法。有人能想到更优雅的东西吗?

4

7 回答 7

13

不幸的是,虚拟克隆/复制模式是您唯一真正的选择。

这有一些变化,但基本上它们都归结为为每种类型编写函数,可以返回一个新副本。

于 2009-01-27T01:55:11.293 回答
4

您需要在层次结构中的每个对象上实现虚拟克隆方法。你的未定义类型的对象怎么知道如何复制自己?

为了使编写派生类的人更清楚这一点,您可能会考虑在您的基类中也使克隆方法抽象,以迫使人们至少编写一些东西。

在我的代码的几个地方,我还测试以确保正确实现 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() 和 to_string() 的一些最低标准。

编辑:更新了代码,并根据 strager 的评论进行了更正。谢谢!

于 2009-01-27T02:08:02.717 回答
2

我不认为这很容易做到。事实上,在 Effective C++ 中,Meyers 警告说,如果你有一个按值传递的函数,并说你有

void foo(Base b)

并且您将 a 传递给Derived1 d1foo副本将被切掉 - 即不会复制派生部分。

但是我现在是从记忆中引用的……

于 2009-01-27T01:51:14.717 回答
0

复制构造函数是不可能的(据我所知),因为在调用构造函数之前分配了内存。使用已知类型的复制构造函数,而不是对象类型。

Base *foo = new Derived1();
Base bar = *foo;               // Base's copy constructor is called.

正如您所建议的,您最好的选择是提供一种clone()方法。

于 2009-01-27T02:09:15.883 回答
0

另一种方法是创建一个类似工厂的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);
        }
};
于 2009-01-27T02:15:43.700 回答
0

如果您想要一个透明的克隆方法,请查看Implicit Sharing

于 2009-01-27T02:16:48.613 回答
0

你是对的clone()。您不能使用构造函数,因为它们强制静态绑定。您不能按值返回对象,因为这再次强制静态绑定。因此,您需要一个返回动态分配对象的虚函数。

于 2009-01-27T02:35:38.480 回答