在简单的示例继承层次结构中:
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,以查找在调用基类克隆函数时要创建的子类。
这些似乎都不是理想的,但我更倾向于第三种解决方案,因为它简化了调用克隆功能,而不需要为每个子类编写定义(为此会有很多)。
但是,我对任何建议都持开放态度,有更好的方法吗?
答案 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"中阅读相关内容。然而,结论是宏观是最实用的。