In the trivial example inheritance hierarchy:
class Food
{
virtual ~Food();
};
class Fruit : public Food
{
virtual ~Fruit();
};
class Apple: public Fruit
{
virtual ~Apple();
}
class Vegetable: public Food
{
virtual ~Vegetable();
}
I wish to create a method that can clone an object from its subclass or baseclass instance:
Apple* apple1 = new Apple();
Apple* clone1 = apple1->clone();
Food* food1 = apple1;
Apple* clone2 = food1->clone();
I see a few possible solutions to the problem:
Use polymorphism to create a virtual function that is explicitly defined for each subclass.
Use a templated factory method to take an instance of a class and the type of the subclass to create the new instance.
Register an id wit开发者_如何学运维h each class and subclass that is stored elsewhere to lookup which subclass to create on calling a baseclass clone function.
None of these seem ideal, but I'm leaning more towards the third solution as it simplifies calling the clone function without requiring a definition to be written for every subclasses (for which there will be a lot).
However, I'm very much open to any suggestions, are there better ways of doing this?
You can use the CRTP to automatically implement a Clone method.
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> {
};
In this case, you can always call Clone() to copy the current object with a fresh allocation on the heap and you don't need to implement it again in any class. Of course, if your Clone semantics need to be different, then you can just alter the function.
Not only can the CRTP implement clone() for you, it can even do it between different inheritance hierarchies.
Your last example, ...
Food* food1 = dynamic_cast<Food*>(apple1);
Apple* clone2 = f1->clone();
... won't work, even with the speling eror corrected. You need the cast the other way:
Food* food1 = apple1;
Apple* clone2 = dynamic_cast<Apple*>( f1->clone() );
Apart from that, the practical solution to cloning in C++ is to define a macro:
#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() \
); \
}
... where OwnershipPtr
might be e.g. std::auto_ptr
.
Then all you have to do is to place a macro invocation in each class that should support cloning. Simple.
It's also possible to implement reusable cloning via templating, but that's more complicated both for the implementation and the usage. You can read about that in my blog posting "3 ways to mix in a generic cloning implementation". The conclusion is, however, that the macro is most practical.
精彩评论