2010-12-09 13 views
48

Consideremos el siguiente ejemplo:Aprendizaje C++: polimorfismo y rebanar

#include <iostream> 
using namespace std; 

class Animal 
{ 
public: 
    virtual void makeSound() {cout << "rawr" << endl;} 
}; 

class Dog : public Animal 
{ 
public: 
    virtual void makeSound() {cout << "bark" << endl;} 
}; 

int main() 
{ 
    Animal animal; 
    animal.makeSound(); 

    Dog dog; 
    dog.makeSound(); 

    Animal badDog = Dog(); 
    badDog.makeSound(); 

    Animal* goodDog = new Dog(); 
    goodDog->makeSound(); 
} 

La salida es:

rawr 
bark 
rawr 
bark 

Pero pensé que seguramente la salida debe ser "rawr corteza corteza corteza". ¿Qué pasa con el badDog?


Actualización: Usted puede estar interesado en another question of mine.

+1

Creo que tiene algunos errores de nomenclatura para sus variables. – aioobe

+2

El código ni siquiera se compila como está. Y 'void main()' ??? Ew ... –

+2

No puedo entender por qué alguien rechazó esta pregunta, no es "poco clara" ni "no útil". +1 para negar el downvote. – casablanca

Respuesta

64

Este es un problema llamado "rebanar".

Dog() crea un objeto Dog. Si llamaras al Dog().makeSound(), imprimiría "ladrar" como esperabas.

El problema es que está inicializando el badDog, que es un objeto del tipo Animal, con este Dog. Como el Animal solo puede contener un Animal y nada derivado de Animal, toma la parte Animal del Dog y se inicializa con eso.

El tipo de badDog es siempre ; nunca puede ser otra cosa.

La única manera en que puede obtener el comportamiento polimórfico en C++ es usando punteros (como lo ha demostrado con su ejemplo goodDog) o utilizando referencias.

Una referencia (por ejemplo, Animal&) puede referirse a un objeto de cualquier tipo derivado de Animal y un puntero (por ejemplo, Animal*) puede apuntar a un objeto de cualquier tipo derivado de Animal. Un simple Animal, sin embargo, siempre es un Animal, nada más.

Algunos lenguajes como Java y C# tienen una semántica de referencia, donde las variables son (en la mayoría de los casos) sólo referencias a los objetos, por lo que dado una Animal rex;, rex es en realidad una referencia a algún Animal y rex = new Dog() hace rex se refieren a un nuevo Dog objeto.

C++ no funciona de esa manera: las variables no se refieren a objetos en C++, las variables son objetos. Si dices rex = Dog() en C++, copia un nuevo objeto Dog en rex, y como rex es en realidad del tipo Animal, se corta y solo se copian las partes Animal. Estos se denominan semántica de valores, que son los predeterminados en C++. Si desea semántica de referencia en C++, necesita usar referencias o punteros explícitamente (ninguno de estos es lo mismo que las referencias en C# o Java, pero son más similares).

+0

Excelente explicación. ¡Gracias! – JnBrymn

+4

Añadiré que simplifiqué demasiado la comparación entre los sistemas tipo de Java y C# y C++. –

+0

+1 a la respuesta y el comentario :) – suszterpatt

7
Animal badDog = Dog(); 
    ad.makeSound(); 

Cuando se instancia un Dog y se asigna por valor a una variable de Animal, que slice el objeto. Lo que básicamente significa que te quitas todo Dog -ness de badDog y lo ingresas a la clase base.

Para utilizar el polimorfismo con las clases base, debe usar punteros o referencias.

+0

+1 por hacerlo corto y constar – starcorn

-1

Inicializó badDog utilizando el operador de asignación. Por lo tanto, Dog() se copió como Animal. La salida de tu programa es correcta. :)