наследование — C ++: глубокое копирование указателя на базовый класс

Я искал вокруг и, кажется, чтобы выполнить это, мне нужно изменить свой базовый класс и хочу знать, является ли это лучшим подходом.
Например,
У меня есть Базовый класс:

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) {}
}

Если предположить, что конструкторы копирования базового класса и класса Derived_x правильно закодированы, то как лучше написать конструктор копирования для DeepCopy. Как мы можем узнать о классе, который находится в basePtr объекта, который мы собираемся скопировать?

Единственный способ думать об этом — использовать RTTI, но использование длинного списка dynamic_casts кажется неправильным. Кроме того, требуется, чтобы DeepCopy знал об иерархии наследования базового класса.

Другой метод, который я видел, это Вот. Но для этого требуются базовые и производные классы, реализующие метод клонирования.

Так есть ли намного более простой, стандартный способ сделать это?

20

Решение

Вам нужно использовать виртуальная копия шаблон: предоставить виртуальную функцию в интерфейсе, который выполняет копирование, а затем реализовать его по всей иерархии:

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() )
{}
};
27

Другие решения

Используя подход, который использует 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())
{}
};
20

Я думаю, что шаблоны — лучший способ пойти в этой ситуации:

template<typename Sub>
class DeepCopy
{
Base *base;

DeepCopy(Sub *sub)
{
base = new Sub(*sub); // use copy constructor
}
}

Это значит, что DeepCopyОни не могут быть назначены друг другу, но это цена, которую вы платите с C ++.

1
По вопросам рекламы [email protected]