2009-01-27 24 views
5

Supongamos que tenemos la siguiente jerarquía de clases:Haga una copia de un tipo concreto desconocido en C++

class Base { 
    ... 
}; 

class Derived1 : public Base { 
    ... 
}; 

class Derived2 : public Base { 
    ... 
}; 

Dado un Base* que podría apuntar a un objeto Derived1 o Derived2 cómo puedo hacer una copia de la real objeto dado que su tipo concreto es desconocido. Pensé en definir constructores de copia, pero no creo que esto sea posible sin conocer los tipos reales involucrados. La única solución en la que puedo pensar es definir un método clone() en cada tipo en la jerarquía. ¿Alguien puede pensar en algo más elegante?

Respuesta

12

Por desgracia, un/patrón clon virtual copia es su única opción real.

Existen variaciones de esto, pero básicamente todas se reducen a funciones de escritura para cada tipo que puede devolver una nueva copia.

2

No creo que sea fácil de hacer. De hecho, en los Efectiva C++, Meyers advertido de que si usted tiene una función que pasan por valor, y decir que tienen

void foo(Base b) 

y se pasa un Derived1 d1 en foo, la copia se cortó - es decir, la las partes derivadas no serán copiadas.

Pero entonces estoy citando de memoria en el momento ...

4

Debe implementar el método de clonación virtual en cada objeto de la jerarquía. ¿De qué otro modo su objeto de tipo indefinido sabrá cómo copiarse?

Para hacer esto más obvio para las personas que escriben clases derivadas, puede pensar en hacer que el método de clonación también sea abstracto en su clase base, para forzar a la gente a escribir al menos algo.

En varios lugares para mi código, también pruebo para asegurarme de que el método to_string() se implemente correctamente para serializar un objeto a una cadena. La prueba específica que he utilizado en mi clase para probar clon y to_string al mismo tiempo se ve así:

Base *obj1, *obj2; 
# initialize obj1 in a reasonable manner 
obj2 = obj1->clone(); 
assert(obj1->to_string() == obj2->to_string()); 

Esto está poniendo a prueba tanto el método clone() y la serialización de objetos a cadenas (lo que no es estrictamente una prueba de unidad), pero es un paradigma tan simple de rodear todos los objetos en una fábrica para asegurarse de que están siguiendo un estándar mínimo de implementación de clone() y to_string().

Editar: Código actualizado con la corrección del comentario de strager. ¡Gracias!

+0

Creo que quieres decir Base * obj1, * obj2 ;. =] – strager

+0

Good catch strager! –

0

Un constructor de copia no es posible (hasta donde yo sé) porque la memoria está asignada antes de se llama al constructor. Se usa el constructor de copia del tipo conocido, no del tipo del objeto.

Base *foo = new Derived1(); 
Base bar = *foo;    // Base's copy constructor is called. 

Su mejor opción sería la de proporcionar un método clone(), como usted sugiere.

-1

Una alternativa es crear una fábrica como Base, suponiendo que conoce todas sus clases derivadas:

Pensándolo bien, esto es probablemente una mala idea!

class Base { 
    public: 
     static Base *clone(const Base *obj) { 
      if((Derived1 *o = dynamic_cast<Derived1 *>(obj)) { 
       return new Derived1(*o); 
      } else if((Derived2 *o = dynamic_cast<Derived2 *>(obj)) { 
       return new Derived2(*o); 
      } 

      /* TODO When more classes are added, put them here. (Probably bad design, yes.) */ 

      return new Base(obj); 
     } 

     Base *clone() const { 
      return clone(this); 
     } 
}; 
+1

Hilarante: ¿por qué publicarlo si es una mala idea? – Tom

+0

@Tom, Bueno, es/probablemente/una mala idea. Estoy seguro de que hay algunos casos excepcionales en que se puede usar ... – strager

+0

Es * ciertamente * una mala idea. Una clase base nunca debería tener conocimiento de sus hijos. Esa es una de las peores violaciones del principio de inversión de dependencia. –

0

Está en la recta sobre clone(). No puede usar constructores porque fuerzan el enlace estático. No puede devolver un objeto por valor porque eso fuerza nuevamente el enlace estático. Por lo tanto, necesita una función virtual que devuelva un objeto dinámicamente asignado.

Cuestiones relacionadas