2011-03-20 14 views
5

digamos que tienen una jerarquía de clases, donde tenemos una clase genérica Animal, que tiene varias clases heredan directamente de ella (como Dog, Cat, Horse, etc ..).clases polimórficas en las plantillas

Al usar plantillas en esta jerarquía de herencia, ¿es legal simplemente usar SomeTemplateClass<Animal> y luego meter en Perros y Gatos y Caballos en este objeto con plantilla?

Por ejemplo, supongamos que tenemos una clase de plantilla Stack, donde queremos alojar todo tipo de animales. ¿Puedo simplemente indicar Stack<Animal> s; Dog d; s.push(d); Cat c; s.push(c);

Respuesta

5

respuesta de su pregunta si No. Pero puede utilizar SomeTemplateClass<Animal*> y pasar punteros de objetos de clases derivadas a ella.

Por ejemplo, si tiene una clase de pila de plantilla, donde desea alojar todo tipo de animales. Simplemente puede hacer lo siguiente:

Stack<Animal*> s; 
Dog d; 
s.push(&d); 
Cat c; 
s.push(&c) 
+0

Para contenedores de punteros, solo puedo recomendar el uso de Boost Pointer Containers como base. Es como punteros inteligentes, pero para objetos múltiples y con la mezcla habitual de propiedades que esperas de STL. –

3

No, tendría que utilizar punteros, es decir, Stack<Animal*> (o algún tipo de puntero inteligente). La razón es que Dog, Cat, Horse etc. no tienen necesariamente el mismo tamaño, ya que pueden agregar variables de miembro.

El contenedor puede asignar espacio que sea lo suficientemente grande como para almacenar un Animal. Si un Dog es más grande que eso, el contenedor intentará copiar y construir un Dog que está insertado en un espacio demasiado pequeño, lo que podría dañar la memoria.

0

NO Stack<Animal> y Stack<Dog> son clases completamente diferentes.

Ni siquiera puede transmitir entre Stack<Animal> y Stack<const Animal>.

Editar: Pero como señaló @Mihran se puede tratar de utilizar Stack<Animal* > lugar Stack<Animal>

0

Depende del uso que haga la plantilla con el tipo pasado. Si se refiere a contenedores estándar (por ejemplo, std::vector, std::map, etc.), la respuesta es no. No existe ninguna relación entre std::vector<Animal> y std::vector<Dog>, incluso si en la jerarquía de su clase los perros provienen de animales.

No se puede poner un Dog en un std::vector<Animal> ... C++ usos copiar semántico y se incurriría en lo que se llama "slicing", que significa que su Dog instancia perderá cualquier miembro que no está presente también en la clase base Animal .

Sin embargo, en general, es muy posible que una plantilla use el tipo de diferentes maneras que permitan aceptar instancias de clases derivadas. Por ejemplo, en el siguiente código, la plantilla MethodCaller se puede crear una instancia con un tipo pero usando una instancia de un tipo derivado y manejando correctamente el envío de enlace tardío. Esto es posible porque la instancia MethodCaller solo contiene una referencia y no hace una copia del objeto.

#include <stdio.h> 

template<typename T> 
struct MethodCaller 
{ 
    T& t; 
    void (T::*method)(); 
    MethodCaller(T& t, void (T::*method)()) 
     : t(t), method(method) 
    {} 
    void operator()() { (t.*method)(); } 
}; 

struct Animal { virtual void talk() = 0; }; 
struct Dog : Animal { virtual void talk() { printf("Bark\n"); } }; 
struct Cat : Animal { virtual void talk() { printf("Meow\n"); } }; 
struct Crocodile : Animal { virtual void talk() { printf("??\n"); } }; 

void makenoise(Animal *a) 
{ 
    MethodCaller<Animal> noise(*a, &Animal::talk); 
    noise(); noise(); noise(); 
} 

int main() 
{ 
    Dog doggie; 
    Cat kitten; 
    Crocodile cocco; 
    makenoise(&doggie); 
    makenoise(&kitten); 
    makenoise(&cocco); 
} 

También es posible implementar la clase Stack como desee ...

#include <vector> 

template<typename T> 
struct Stack 
{ 
    std::vector<T *> content; 
    ~Stack() 
    { 
     for (int i=0,n=content.size(); i<n; i++) 
      delete content[i]; 
    } 

    template<class S> 
    void push(const S& s) 
    { 
     content.push_back(new S(s)); 
    } 

    template<class S> 
    S pop() 
    { 
     S result(dynamic_cast<S&>(*content.back())); 
     content.pop_back(); 
     return result; 
    } 

private: 
    // Taboo 
    Stack(const Stack&); 
    Stack& operator=(const Stack&); 
}; 

int main() 
{ 
    Dog doggie; 
    Cat kitten; 
    Crocodile cocco; 

    Stack<Animal> s; 
    s.push(doggie); 
    s.push(kitten); 
    s.push(cocco); 

    Crocodile cocco2 = s.pop<Crocodile>(); 
    Cat kitten2 = s.pop<Cat>(); 
    Dog doggie2 = s.pop<Dog>(); 
} 

Tenga en cuenta que en la aplicación que he usado un std::vector para mantener punteros a los animales y por lo tanto para evitar el problema de corte. He estado usando un método de plantilla para poder aceptar tipos derivados en la llamada de inserción.

También tenga en cuenta que cuando saltan los animales debe proporcionar lo que es la clase y si es una persona equivocada (por ejemplo, que el pop a cabo una Crocodile cuando el elemento superior de la pila es una Dog) obtendrá una excepción bad_cast en tiempo de ejecución.

Cuestiones relacionadas