在C ++中干净地复制基类或子类的实例?

时间:2010-10-23 13:18:38

标签: c++ templates refactoring polymorphism factory

在简单的示例继承层次结构中:

class Food
{
    virtual ~Food();
};

class Fruit : public Food
{
    virtual ~Fruit();
};

class Apple: public Fruit
{
    virtual ~Apple();
}

class Vegetable: public Food
{
    virtual ~Vegetable();
}

我希望创建一个可以从其子类或基类实例克隆对象的方法:

Apple* apple1 = new Apple();
Apple* clone1 = apple1->clone();

Food* food1 = apple1;
Apple* clone2 = food1->clone();

我看到了一些可能解决问题的方法:

  • 使用多态来创建为每个子类显式定义的虚函数。

  • 使用模板化工厂方法获取类的实例和子类的类型以创建新实例。

  • 为存储在别处的每个类和子类注册一个id,以查找在调用基类克隆函数时要创建的子类。

这些似乎都不是理想的,但我更倾向于第三种解决方案,因为它简化了调用克隆功能,而不需要为每个子类编写定义(为此会有很多)。

但是,我对任何建议都持开放态度,有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

您可以使用CRTP自动实现克隆方法。

template<typename T, typename Derive> class CloneImpl : public Derive {
public:
    virtual Derive* clone() {
        return new T(static_cast<const T&>(*this));
    }
};
class Food {
public:
    virtual Food* clone() = 0;
    virtual ~Food() {}
};
class Fruit : public Food {
};
class Dairy : public Food {
};
class Apple : public CloneImpl<Apple, Fruit> {
};
class Banana : public CloneImpl<Banana, Fruit> {
};
class Cheese : public CloneImpl<Cheese, Dairy> {
};
class Milk : public CloneImpl<Milk, Dairy> {
};

在这种情况下,您始终可以调用Clone()来复制当前对象,并在堆上进行全新分配,而无需在任何类中再次实现它。当然,如果您的克隆语义需要不同,那么您可以改变函数。

CRTP不仅可以为您实现clone(),甚至可以在不同的继承层次结构之间实现。

答案 1 :(得分:-3)

你的最后一个例子,......

Food* food1 = dynamic_cast<Food*>(apple1);
Apple* clone2 = f1->clone();
即使纠正了拼写错误,

......也行不通。你需要另一种方式:

Food* food1 = apple1;
Apple* clone2 = dynamic_cast<Apple*>( f1->clone() );

除此之外,在C ++中克隆的实用解决方案是定义一个宏:

#define YOURPREFIX_IMPLEMENT_CLONING( Class )                       \
    virtual Class*                                                  \
        virtualCloneThatIsUnsafeToCallDirectly() const              \
    {                                                               \
        assert( typeid( *this ) == typeid( Class ) );               \
        return new Class( *this );                                  \
    }                                                               \
                                                                    \
    OwnershipPtr< Class >                                           \
        clone() const                                               \
    {                                                               \
        return OwnershipPtr< Class >(                               \
            virtualCloneThatIsUnsafeToCallDirectly()                \
            );                                                      \
    }

...其中OwnershipPtr可能是std::auto_ptr

然后,您所要做的就是在每个应该支持克隆的类中进行宏调用。简单。

也可以通过模板实现可重用克隆,但这对于实现和使用来说都更复杂。您可以在我的博客"3 ways to mix in a generic cloning implementation"中阅读相关内容。然而,结论是宏观是最实用的。