【问题标题】:Make a copy of an unknown concrete type in c++在 C++ 中复制一个未知的具体类型
【发布时间】:2010-10-03 16:23:00
【问题描述】:

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

class Base {
    ...
};

class Derived1 : public Base {
    ...
};

class Derived2 : public Base {
    ...
};

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

【问题讨论】:

    标签: c++ inheritance polymorphism


    【解决方案1】:

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

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

    在我的代码的几个地方,我还测试以确保正确实现 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 的评论进行了更正。谢谢!

    【讨论】:

    • 我想你的意思是 Base *obj1, *obj2;。 =]
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        另一种方法是创建类似工厂的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);
                }
        };
        

        【讨论】:

        • 搞笑 - 如果这是一个坏主意,为什么要发布它?
        • @Tom,好吧,这 / 可能 / 是个坏主意。我敢肯定有一些罕见的情况可以使用它......
        • 当然是个坏主意。基类从不知道它的子类——这是对依赖倒置原则最严重的违反之一。
        【解决方案5】:

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

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

        按照您的建议,最好的办法是提供clone() 方法。

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

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

            void foo(Base b)
            

            如果您将 Derived1 d1 传递给 foo,则副本将被切掉 - 即不会复制派生部分。

            但是我现在是从记忆中引用...

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-09-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-09-25
              • 1970-01-01
              相关资源
              最近更新 更多