2012-09-04 20 views
18

He buscado y parece que para poder realizar esto necesito cambiar mi clase Base y quiero saber si este es el mejor enfoque. Por ejemplo, que tienen una clase base:C++: Copia profunda de un puntero de clase base

class Base {} 

A continuación, una larga lista 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 base Los constructores de clase y Derived_x class copy están codificados correctamente, ¿cuál es la mejor manera de escribir el constructor de copia para DeepCopy? ¿Cómo podemos saber sobre la clase que está en la basePtr del objeto que vamos a copiar?

La única forma en que puedo pensar es en usar RTTI, pero el uso de una larga lista de dynamic_casts parece no estar bien. Además, requiere que DeepCopy sepa sobre la jerarquía de herencia de la clase Base.

El otro método que vi es here. Pero requiere que las clases Base y Derivadas implementen un método de clonación.

¿Existe una manera más fácil y estándar de hacer esto?

+0

Si se utiliza un tipo de datos POD, diría 'memcpy', pero ya que no es así, podría usar plantillas. –

Respuesta

24

Es necesario utilizar el virtual de copia patrón: proporcionar una función virtual en la interfaz que lo hace la copia y luego ponerlo en práctica a través de 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 sólo tiene que llamar a ese función:

class DeepCopy 
{ 
    Base * basePtr;  
public: 
    DeepCopy(DeepCopy const & dc)   // This should be `const` 
     : basePtr(dc.basePtr->clone()) 
    {} 
}; 
+0

Gracias. ¿No necesitamos devolver esto * en clon de clase base()? – madu

+1

@madu Si desea tener objetos reales de la clase base, debe implementar 'base :: clone' de la misma manera que para la clase derivada:' return new base (* this); '.Si desea utilizar la clase base solo como una clase base, pero no para crear una instancia de los objetos reales, mejor omita su definición estableciendo 'virtual base * clone() const = 0;'. – jogojapan

+0

@madu: Correcto, eso fue un error en la respuesta. La función de miembro virtual debe ser pura o correctamente implementada. –

1

Creo que las plantillas son el mejor camino a seguir en esta situación:

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

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

Esto significa que DeepCopy no se pueden asignar entre sí, pero ese es el precio que paga con C++.

19

El uso de un enfoque que emplea una función clone() es una buena solución. Nota usando el CRTP (the curiously recurring template pattern) puede ahorrarle parte del trabajo. La forma de hacerlo es mediante la introducción de un nivel intermedio (llamado BaseCRTP a continuación) que es una plantilla e implementa la función clone(). Cuando deriva 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 para ellos automáticamente. Asegúrese de que las clases derivadas implementen un constructor de copia (o asegúrese de que el valor predeterminado es el 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... */ 
}; 

Puede entonces obviamente implementar la clase DeepCopy de la forma habitual:

class DeepCopy 
{ 
    Base *basePtr;  
public: 
    DeepCopy(const DeepCopy &dc) 
    : basePtr(dc.basePtr->clone()) 
    {} 
}; 
+0

no sabía acerca de crtp, buen truco :) –

+1

más uno para la pulcritud de esto. Esta es la manera más elegante que he visto esto hecho. – namezero

+0

@jogojapan Elegante +1. ¿Qué ocurre si necesitamos heredar Derived11 de Derived1? ¿Sobrescribir la función de clonar con el argumento de la plantilla Derived11 o hay una forma adecuada? – ataman

Cuestiones relacionadas