2010-12-09 78 views
48

Prenons l'exemple suivant:apprentissage C++: polymorphisme et tranchage

#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 sortie est:

rawr 
bark 
rawr 
bark 

Mais je pensais que sûrement la sortie devrait être "l'écorce de rawr écorce d'écorce". Qu'est-ce qu'il y a avec le baddog?


Mise à jour: Vous pouvez être intéressé par another question of mine.

+1

Je pense que vous avez des erreurs de nommage pour vos variables. – aioobe

+2

Le code n'est même pas compilé tel quel. Et 'void principal()' ??? Ew ... –

+2

Je n'arrive pas à comprendre pourquoi quelqu'un aurait rejeté cette question - ce n'est ni "pas clair" ni "pas utile". +1 pour annuler la baisse. – casablanca

Répondre

64

Il s'agit d'un problème appelé "découpage".

Dog() crée un objet Dog. Si vous appelez le Dog().makeSound(), il affichera "aboyer" comme vous le souhaitez. Le problème est que vous initialisez le badDog, qui est un objet de type Animal, avec ce Dog. Puisque le Animal ne peut contenir qu'un Animal et aucun élément dérivé de Animal, il prend la partie Animal du Dog et s'initialise avec cela. Le type de badDog est toujours Animal; ça ne peut jamais être autre chose. La seule façon d'obtenir un comportement polymorphique en C++ est d'utiliser des pointeurs (comme vous l'avez démontré avec votre exemple goodDog) ou d'utiliser des références.

une référence (par exemple, Animal&) peut se référer à un objet de tout type dérivé de Animal et un pointeur (par exemple, Animal*) peut pointer vers un objet de tout type dérivé de Animal. Un simple Animal, cependant, est toujours un Animal, rien d'autre.

Certains langages comme Java et C# ont une sémantique de référence, où les variables sont (dans la plupart des cas) seulement des références à des objets, ainsi donné un Animal rex;, rex est vraiment juste une référence à une Animal et rex = new Dog() fait rex se réfèrent à une nouvelle Dog objet.

C++ ne fonctionne pas de cette façon: les variables ne se réfèrent pas aux objets en C++, les variables sont des objets. Si vous dites rex = Dog() en C++, il copie un nouvel objet Dog en rex, et puisque rex est en fait de type Animal, il est découpé et seules les parties Animal sont copiées. Ce sont les sémantiques de valeur, qui sont les valeurs par défaut en C++. Si vous voulez une sémantique de référence en C++, vous devez utiliser explicitement des références ou des pointeurs (ni l'un ni l'autre ne sont identiques aux références en C# ou Java, mais ils sont plus similaires).

+0

Excellente explication. Merci! – JnBrymn

+4

J'ajouterai que j'ai grossièrement simplifié à l'extrême la comparaison entre les systèmes de types Java et C# et C++. –

+0

+1 à la réponse et au commentaire :) – suszterpatt

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

Lorsque vous instanciez un Dog et affectez par valeur à une variable Animal, vous slice l'objet. Ce qui signifie essentiellement que vous dépouiller tous les Dog -ness de badDog et le rendre dans la classe de base.Pour utiliser le polymorphisme avec les classes de base, doit utiliser des pointeurs ou des références.

+0

+1 pour le rendre court et consister – starcorn

-1

Vous avez initialisé badDog en utilisant l'opérateur d'affectation. Ainsi Dog() a été copié comme animal. La sortie de votre programme est correcte. :)