С++: глубокое копирование указателя базового класса
Я искал вокруг и, кажется, для того, чтобы выполнить это, мне нужно изменить свой базовый класс и узнать, подходит ли это лучший подход.
Например,
У меня есть базовый класс:
class Base {}
Затем длинная строка производных классов:
class Derived_1:: public Base {}
class Derived_2:: public Derived_1{}
...
...
class Derived_n:: public Derived_M{}
И у меня есть еще один класс:
class DeepCopy
{
Base * basePtr;
public:
DeepCopy(DeepCopy & dc) {}
}
Предполагая, что конструкторы копирования класса Base и Derived_x правильно закодированы, как лучше всего написать конструктор копирования для DeepCopy. Как мы можем узнать о классе, который находится в basePtr объекта, который мы собираемся скопировать?
Единственный способ, которым я могу думать, - использовать RTTI, но использование длинного списка dynamic_casts кажется неправильным. Кроме того, DeepCopy требует знать иерархию наследования базового класса.
Другой метод, который я видел, здесь. Но для этого требуется, чтобы классы Base и Derived реализовали метод clone.
Итак, есть гораздо более простой, стандартный способ сделать это?
Ответы
Ответ 1
Вам нужно использовать шаблон виртуальной копии: предоставить виртуальную функцию в интерфейсе, который выполняет копию, а затем реализовать ее по иерархии:
struct base {
virtual ~base() {} // Remember to provide a virtual destructor
virtual base* clone() const = 0;
};
struct derived : base {
virtual derived* clone() const {
return new derived(*this);
}
};
Затем объект DeepCopy
должен просто вызвать эту функцию:
class DeepCopy
{
Base * basePtr;
public:
DeepCopy(DeepCopy const & dc) // This should be `const`
: basePtr( dc.basePtr->clone() )
{}
};
Ответ 2
Использование подхода, использующего функцию clone()
, является хорошим решением. Обратите внимание, что с помощью CRTP (любопытно повторяющийся шаблон шаблона) может сэкономить вам часть работы. То, как вы это делаете, - это ввести промежуточный уровень (называемый BaseCRTP
ниже), который является шаблоном и реализует функцию clone()
. Когда вы получаете свои фактические классы, используйте их как аргумент шаблона базы, из которой они получены. Они получат функцию clone()
, реализованную для них автоматически. Убедитесь, что производные классы реализуют конструктор копирования (или убедитесь, что по умолчанию это то, что вам нужно).
/* Base class includes pure virtual clone function */
class Base {
public:
virtual ~Base() {}
virtual Base *clone() const = 0;
};
/* Intermediate class that implements CRTP. Use this
* as a base class for any derived class that you want
* to have a clone function.
*/
template <typename Derived>
class BaseCRTP : public Base {
public:
virtual Base *clone() const {
return new Derived(static_cast<Derived const&>(*this));
}
};
/* Derive further classes. Each of them must
* implement a correct copy constructor, because
* that is used by the clone() function automatically.
*/
class Derived1 : public BaseCRTP<Derived1> {
/*... should have an ordinary copy constructor... */
};
class Derived2 : public BaseCRTP<Derived2> {
/*... should have an ordinary copy constructor... */
};
Вы можете, очевидно, реализовать класс DeepCopy
обычным способом:
class DeepCopy
{
Base *basePtr;
public:
DeepCopy(const DeepCopy &dc)
: basePtr(dc.basePtr->clone())
{}
};
Ответ 3
Я думаю, что шаблоны - лучший способ пойти в этой ситуации:
template<typename Sub>
class DeepCopy
{
Base *base;
DeepCopy(Sub *sub)
{
base = new Sub(*sub); // use copy constructor
}
}
Это означает, что DeepCopy
не назначаются друг другу, но цена, которую вы платите с помощью С++.