Предложенный «экземпляр», чаще называемый «клон», является нормальным подходом.Альтернативой может быть фабрика и диспетчеризация с использованием rtti для поиска правильного обработчика, который затем вызывает конструктор копирования для производного типа.
struct Abc
{
virtual void who() const = 0;
};
struct A : Abc
{
virtual void who() const { std::cout << "A" << std::endl;}
};
template<class T>
Abc* clone(Abc* abc)
{
T* t = dynamic_cast<T*>(abc);
if (t == 0)
return 0;
return new T(*t);
}
struct B : Abc
{
virtual void who() const { std::cout << "B" << std::endl;}
};
typedef Abc* (*Cloner)(Abc*);
std::map<std::string, Cloner> clones;
void defineClones()
{
clones[ typeid (A).name() ] = &clone<A>;
clones[ typeid (B).name() ] = &clone<B>;
}
Abc* clone(Abc* abc)
{
Abc* ret = 0;
const char* typeName = typeid(*abc).name();
if (clones.find(typeName) != clones.end())
{
Cloner cloner = clones[typeName];
ret = (*cloner)(abc);
}
return ret;
}
void test ()
{
defineClones();
Abc* a = new A;
Abc* anotherA = clone(a);
anotherA->who();
Abc* b = new B;
Abc* anotherB = clone(b);
anotherB->who();
}
Хотя вышеприведенное работает, просто факт, что он использует rtti, будет достаточным дляубедить большинство идти нормальным подходом.Однако, если есть причина, препятствующая изменениям базового класса, это может быть полезно.
Это эффективно?Предельные затраты на добавление нового типа по-настоящему однострочны.Суть в том, что будет легко забыть добавить эту строку с каждым новым классом.Или вы можете видеть положительным моментом то, что весь код клона находится в одном файле, и нам не нужно изменять поддерживаемую иерархию для его обработки.