C++: Copia profunda de un puntero de clase Base
inheritance copy-constructor (3)
Busqué y parece que para realizar esto necesito cambiar mi clase Base y quiero saber si este es el mejor enfoque. Por ejemplo, tengo una clase base:
class Base {}
Luego una larga línea de clases derivadas:
class Derived_1:: public Base {}
class Derived_2:: public Derived_1{}
...
...
class Derived_n:: public Derived_M{}
Y luego tengo otra clase:
class DeepCopy
{
Base * basePtr;
public:
DeepCopy(DeepCopy & dc) {}
}
Suponiendo que la clase base y los constructores de copia de la clase Derived_x estén codificados correctamente, ¿cuál es la mejor manera de escribir el constructor de copia para DeepCopy? ¿Cómo podemos saber acerca de la clase que está en la basePtr del objeto que vamos a copiar?
La única forma en que puedo pensar es usar RTTI, pero usar una larga lista de dynamic_casts no parece correcto. Además, se requiere que DeepCopy sepa sobre la jerarquía de herencia de la clase Base.
El otro método que vi está here . Pero requiere que las clases Base y Derivada implementen un método de clonación.
Entonces, ¿hay una manera mucho más fácil y estándar de hacer esto?
Creo que las plantillas son la mejor manera de ir en esta situación:
template<typename Sub>
class DeepCopy
{
Base *base;
DeepCopy(Sub *sub)
{
base = new Sub(*sub); // use copy constructor
}
}
Esto significa que los de DeepCopy
no se pueden DeepCopy
entre sí, pero ese es el precio que pagas con C ++.
Debe usar el patrón de copia virtual : proporcione una función virtual en la interfaz que hace la copia y luego implementarla en toda la jerarquía:
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);
}
};
Entonces el objeto DeepCopy
solo necesita llamar a esa función:
class DeepCopy
{
Base * basePtr;
public:
DeepCopy(DeepCopy const & dc) // This should be `const`
: basePtr( dc.basePtr->clone() )
{}
};
Usar un enfoque que emplee una función clone()
es una buena solución. Tenga en cuenta que usar el CRTP (el patrón de plantilla curiosamente recurrente) puede ahorrarle parte del trabajo . La forma de hacerlo es introduciendo un nivel intermedio (llamado BaseCRTP
continuación) que es una plantilla e implementa la función clone()
. Cuando derive sus clases reales, utilícelas como el argumento de plantilla de la base de la que se derivan. Obtendrán la función clone()
implementada automáticamente. Asegúrese de que las clases derivadas implementen un constructor de copia (o asegúrese de que el valor predeterminado sea lo que necesita).
/* 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... */
};
Obviamente, puede implementar la clase DeepCopy
de la forma habitual:
class DeepCopy
{
Base *basePtr;
public:
DeepCopy(const DeepCopy &dc)
: basePtr(dc.basePtr->clone())
{}
};