Exercice 0 : reprise de l'exemple du cours (Héritage multiple, niveau 0)
Introduction
Le but de cet exercice est de reprendre l'exemple du cours illustrant la notion d'héritage multiple et de classe virtuelle en approfondissant l'exemple des ovovivipares (vous auriez préférez les flots [streams] ?).
[Essayez de le faire par vous même chaque étape avant de regarder la solution qui suit]
Dans le fichier zoo.cc, commencez par définir une classe Vivipare contenant un entier non signé représentant la durée de gestation (en jours) et un constructeur permettant d'initialiser cette valeur et pouvant servir de constructeur par défaut (avec une valeur de votre choix).
Solution :
- la classe :
addCodefromFile("code09-0-1.cc",false,false); ?>
- l'attribut :
addCodefromFile("code09-0-2.cc",false,false); ?>
- le constructeur :
addCodefromFile("code09-0-3.cc",false,false); ?>
Définissez maintenant une classe Ovipare ayant un attribut représentant le nombre d'oeufs par ponte et un constructeur adapté.
Solution :
Ajoutez à chacune des classe une méthode naissance
affichant
"Après X jours de gestation, je viens de
mettre au monde
un nouveau bébé."
dans le cas d'un vivipare et
"Je viens de pondre environ X oeuf(s)."
dans le cas d'un ovipare, où X correspond à
la valeur de
l'attribut.
La solution est ici vraiment triviale, je continue donc...
Définissez maintenant la classe Ovovivipare comme héritant à la fois de Vivipare (en premier) et de Ovipare.
Ajoutez à cette classe un attribut de type booléen indiquant si l'espèce est rare ou non.
Ajoutez également un constructeur prenant une période de gestation, un nombre d'oeufs et un booléen (faux par défaut) indiquant la rareté de l'espèce.
Solution :
- Pour l'héritage multiple, il suffit de mettre les différentes
super-classes les unes après les autres séparées par des virgules :
addCodefromFile("code09-0-5.cc",false,false); ?>
- On ajoute ensuite le constructeur :
addCodefromFile("code09-0-6.cc",false,false); ?>
Pour terminer cette première exploration, faite afficher un message par les constructeurs des classes Vivipare et Ovipare afin d'observer l'ordre des appels, et créez un main() contenant une instance d'Ovovivipare.
Quel est l'ordre d'appel des constructeurs ?
Changez l'ordre des constructeurs dans le constructeur
d'Ovovivipare. Recompilez et relancer votre programme.
Cela change-t-il l'ordre d'appel ?
Appelez maintenant la méthode naissance de votre instance.
Compilez et testez votre programme
Que se passe-t-il ?
Corriger le programme pour que ce soit la méthode naissance de Vivipare qui soit appelée.
Recompilez et relancer votre programme.
Modifiez finalement le programme pour que la méthode
naissance d'Ovovivipare affiche
"Après X jours de gestation, je viens de
mettre au monde
Y nouveau(x) bébé(s)."
où X correspond à
la période de gestation et Y
au nombre d'oeufs.
Solution :
- Au début de cette étape, on arrive donc au code suivant :
addCodefromFile("code09-0-7.cc",false); ?>
- L'ordre d'appel des constructeurs est toujours celui de la
déclaration des sur-classes dans la déclaration de la classe et
ne dépend pas de l'ordre dans la déclaration du
constructeurs.
D'ailleurs avec le compilateur utilisé ici, vous avez un message d'alerte (« warning ») indiquant que l'ordre d'appel sera changé si vous ne mettez pas les appels des constructeurs dans l'ordre de la déclaration des héritages.
- L'appel à la méthode naissance(), sans
autre, provoque une erreur de compilation en raison de l'ambiguïté
du nom du à l'héritage multiple :
zoo.cc:48: request for member `naissance' is ambiguous zoo.cc:24: candidates are: void Ovipare::naissance() const zoo.cc:10: void Vivipare::naissance() const
Il faut donc lever cette ambiguïté.
- Une première solution peut être d'utiliser celle de Vivipare en utilisant la directive using (dans la clase Ovovivipare) :
addCodefromFile("code09-0-8.cc",false,false); ?>
Attention, sans parenthèses !
- On peut aussi carrément redéfinir cette méthode dans la classe
Ovovivipare (attention d'enlever le
using précédent) :
addCodefromFile("code09-0-9.cc",false,false); ?>
Je voudrais finir par un exemple de classe virtuel en introduisant la classe Animal.
Ajoutez une classe Animal au programme, ne comprenant qu'un constructeur (par défaut) et un destructeur affichant chacun un message.
Faîtes hériter Vivipare et Ovipare de Animal.
Recompiler et exécuter votre programme.
Que se passe-t-il ?
Pour éviter cela faite que la classe Animal soit virtuelle.
Recompiler et exécuter votre programme.
Notez bien quand le constructeur de Animal est appelé.
Solution :
-
addCodefromFile("code09-0-10.cc",false,false); ?>
- En l'état, on voit bien que DEUX instances de Animal sont créées (pour pourtant un seul Ovovivipare).
Cela est du au fait qu'aucune des deux relation d'héritage n'est virtuelle.
- Pour en rendre une virtuelle, il suffit de faire
addCodefromFile("code09-0-11.cc",false,false); ?>
- Le constructeur d'Animal est bien appelé en
premier~:
coucou, un animal de plus je suis un vivipare je suis un ovipare ...
Vous pouvez trouver ici le code complet de l'exemple.