Big Tuto : Apprenez le C++

Chapitre 28 : Polymorphisme (2)

 

Tutoriel présenté par : Robert Gillard (Gondulzac)
Date de publication : 23 février 2019
Date de révision : -

 

Retrouvez les projets complets de ce chapitre :

  

 

 

  1.0 – L'Héritage multiple (suite)

     1.1 – Constructeurs surchargés dans l'héritage multiple

 

   Nous allons reprendre l'exemple 162HeritageMultiple_1 du chapitre précédent en y ajoutant des constructeurs surchargés comme nous l'avons vu dans les chapitres sur l'introduction aux classes. Cela va en outre nous permettre de procéder à quelques révisions tout en adaptant ces notions à la notion d'héritage. wink

   Nous travaillons donc avec trois classes, la classe Archer_de_cavalerie héritant des deux classes de base Eclaireur et Archer.

   Projet 163HeritageMultiple_2

      Fichier : Eclaireur.h

 

//Projet 163HeritageMultiple_2
//Constructeurs et méthodes membres surchargés dans l'héritage multiple
//Fichier : Eclaireur.h
 
#include <iostream>
#include <string>
 
using uint = unsigned int;
 
class Eclaireur
{
public:
 
//constructeurs
Eclaireur() {}
Eclaireur(std::string nom) { _nom = nom; }
 
Eclaireur(std::string nom, std::string race) { _nom = nom, _race = race; }
Eclaireur(std::string nom, std::string race, std::string arme) { _nom = nom, _race = race, _arme = arme; }
 
//Destructeur
virtual ~Eclaireur() {}
 
//Méthodes membres d'accès publique
std::string get_Name() { return _nom; }
std::string get_Race() { return _race; }
std::string get_Arm() { return _arme; }
uint get_Life() { return _qte_Vie; }
 
//Autres méthodes
virtual void Se_Presenter() const
{
std::cout << "Je suis un eclaireur a cheval, j'emmene la troupe !" << std::endl;
}
 
void Galoper() const { std::cout << "Je galope !" << std::endl; }
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void frapperAvecLesPoings(Eclaireur &autre) const
{
autre.recevoirDegats(2);
}
 
void coup_d_Epee(Eclaireur &autre) const
{
autre.recevoirDegats(10);
}
 
 
protected:
 
std::string _nom;
std::string _race;
std::string _arme;
uint _qte_Vie = 100;
 
}; 

 

       Fichier : Archer.h

 

//Projet 163HeritageMultiple_2
//Constructeurs et méthodes membres surchargés dans l'héritage multiple
//Fichier : Archer.h
 
#include <string>
 
using uint = unsigned int;
 
class Archer
{
public:
 
//constructeurs
Archer() {}
Archer(std::string nom) { _nom = nom; }
Archer(std::string nom, std::string race) { _nom = nom, _race = race; }
Archer(std::string nom, std::string race, std::string arme) { _nom = nom, _race = race, _arme = arme; }
 
//Destructeur
virtual ~Archer() {}
 
//Méthodes membres d'accès publique
std::string get_Name() { return _nom; }
std::string get_Race() { return _race; }
std::string get_Arm() { return _arme; }
uint get_Life() { return _qte_Vie; }
 
//Autres méthodes
 
virtual void Se_Presenter() const
{
std::cout << "Je suis un archer, je defends la cite !" << std::endl;
}
 
void Courir() const { std::cout << "Je cours !" << std::endl; }
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void frapperAvecLesPoings(Archer &autre) const
{
autre.recevoirDegats(2);
}
 
void Tir_a_l_arc(Archer &autre) const
{
autre.recevoirDegats(12);
}
 
 
protected:
 
std::string _nom;
std::string _race;
std::string _arme;
uint _qte_Vie = 100;
 
};  

 

      Fichier : Archer_de_cavalerie.h

 

//Projet 163HeritageMultiple_2
//Constructeurs et méthodes membres surchargés dans l'héritage multiple
//Fichier : Archer de cavalerie.h
 
#include <iostream>
#include <string>
//#include "Eclaireur.h"
//#include "Archer.h"
 
using uint = unsigned int;
 
 
class Archer_de_cavalerie : public Eclaireur, public Archer
{
 
public:
 
//constructeurs
Archer_de_cavalerie() { Eclaireur(), Archer(); }
Archer_de_cavalerie(std::string nom) { _nom = nom, Eclaireur(nom), Archer(nom); }
Archer_de_cavalerie(std::string nom, std::string race) { _nom = nom, _race = raceEclaireur(nom, race), Archer(nom, race); }
Archer_de_cavalerie(std::string nom, std::string race, std::string arme{ _nom = nom, _race = race, _arme = arme, Eclaireur(nom, race, arme), Archer(nomrace, arme); }
 
//Destructeur
virtual ~Archer_de_cavalerie() {}
 
//Méthodes membres d'accès publique
std::string get_Name() { return _nom; }
std::string get_Race() { return _race; }
std::string get_Arm() { return _arme; }
uint get_Life() { return _qte_Vie; }
 
 
//Autres méthodes
 
virtual void Se_Presenter() const override
{
std::cout << "Je suis un archer a cheval, je me deplace rapidement !" << std::endl;
}
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void Tir_a_l_arc(Archer &autre) const
{
autre.recevoirDegats(14);
}
 
void Tir_a_l_arc(Eclaireur &autre) const
{
autre.recevoirDegats(14);
}
 
protected:
 
std::string _nom;
std::string _race;
std::string _arme;
uint _qte_Vie = 100;
 
}; 

 

 Fichier : HeritageMultiple_2.cpp

 

//Projet 163HeritageMultiple_2
//Constructeurs et méthodes membres surchargés dans l'héritage multiple
//Fichier : HeritageMultiple_2.cpp
#include <iostream>
#include <conio.h>
 
#include "Eclaireur.h"
#include "Archer.h"
 
#include "Archer_de_cavalerie.h"
 
using namespace std;
 
int main()
{
 
cout << endl;
cout << "Creation des objets a l'aide des differents constructeurs surcharges" <<
endl << endl;
 
Eclaireur eclaireur1("Tarek");
Eclaireur eclaireur2("Urak", "orc");
Eclaireur eclaireur3("Raniek", "orc", "Epee courte");
 
Archer archer1("Wellaen");
Archer archer2("Ellen", "elfe");
Archer archer3("Elwyn", "elfe", "arc long");
Archer archer4("Thiellawen", "elfe", "arc long");
 
Archer_de_cavalerie cavalier1("Rupert");
Archer_de_cavalerie cavalier2("Emmerich", "humain");
Archer_de_cavalerie cavalier3("Hubert", "humain", "arc composite");
 
cout << "Quelques echanges de coups..." << endl;
cout << "Les pdV initiaux de chaque combattant sont de 100" << endl << endl;
cout << eclaireur3.get_Name() << " est un eclaireur " << eclaireur3.get_Race() << endl;
cout << "Il est arme d'un " << eclaireur3.get_Arm() << endl;
cout << archer3.get_Name() << " est un archer " << archer3.get_Race() << endl;
cout << "Il est arme d'un " << archer3.get_Arm() << endl;
cout << archer4.get_Name() << " est une archere " << archer4.get_Race() << endl;
cout << "Elle est armee d'un " << archer4.get_Arm() << endl;
cout << cavalier3.get_Name() << " est un archer de cavalerie " << cavalier3.get_Race() << endl;
cout << "Il est arme d'un " << cavalier3.get_Arm() << endl << endl;
 
cout << "En embuscade, les elfes Elwyn et Thiellawen decochent chacun une fleche " << endl;
cout << "vers l'archer de cavalerie Hubert qui se dirige vers eux (- 2*12 pdV)" << endl;
archer3.Tir_a_l_arc(cavalier3);
archer4.Tir_a_l_arc(cavalier3);
cout << "Les pdV d'Hubert sont desormais de " << cavalier3.get_Life() << endl << endl;
cout << "Hubert est blesse mais il se ressaisit et tire a son tour sur Elwyn (- 14 pdV)" << endl;
cavalier3.Tir_a_l_arc(archer3);
cout << "Les pdV d'Elwyn sont maintenant de " << archer3.get_Life() << endl << endl;
 
cout << "Profitant de la confusion, Raniek s'est avance et donne un coup d'epee a Hubert (- 10 pdV)" << endl;
eclaireur3.coup_d_Epee(cavalier3);
cout << "Les pdV d'Hubert sont maintenant de " << cavalier3.get_Life() << endl << endl;
 
cout << "Surpris, Hubert peut neanmoins decocher une fleche a Raniek (-14 pdV)" << endl;
cavalier3.Tir_a_l_arc(eclaireur3);
cout << "Les pdV de Raniek sont desormais de " << eclaireur3.get_Life() << endl << endl;
 
cout << "Se sentant depasse par le nombre, Hubert s'enfuit..." << endl;
cavalier3.Galoper();
 
_getch();
return 0;
} 

 

     Voici donc un exemple d'héritage multiple. La Classe Archer_de_cavalerie hérite des classes Eclaireur et Archer, ce qui veut dire que la classe dérivée devrait hériter de toutes les fonctionnalités des deux classes de base mais dans l'exemple présenté ce n'est pas tout-à-fait le cas. undecided

   Mais ce programme fonctionne parfaitement me direz-vous ? laughing Oui, c'est le cas mais vous aurez remarqué que la classe Archer_de_cavalerie redéfinit les méthodes d'accès publique suivantes wink :  

 

//Méthodes membres d'accès publique
std::string get_Name() { return _nom; }
std::string get_Race() { return _race; }
std::string get_Arm() { return _arme; }
uint get_Life() { return _qte_Vie; } 

 

     Ces méthodes étant dérivées des classes Eclaireur et Archer, n'aurions nous pas pu nous en passer dans la classe Archer_de_cavaleriesurprised

   Et bien non, du moins pas avec notre exemple écrit tel quel. En effet, si nous supprimons ces méthodes dans la classe dérivée (ou si nous les mettons en remarque par exemple), le compilateur nous signalera une ambiguïté car il ne pourra pas deviner la méthode de quelle classe utiliser. undecided

   Supposons dans ce cas que nous ayons l'instruction suivante dans la fonction main() :

cavalier3.get_Name() ;

   L'objet cavalier3 appelle la méthode get_Name() d'une des deux classes mères, mais de laquelle ? surprised

   Le compilateur ne pourra pas répondre à cette question ! tongue-out

   Un autre problème se présente avec notre projet. Vous vous apercevrez que nous n'avons pas fait combattre ensemble un éclaireur et un archer par exemple. Nous n'aurions pas pu ; en effet les classes Eclaireur et Archer n'ont rien en commun et un objet créé par l'une ne pourrait pas avoir accès à une méthode définie dans l'autre. frown

   Oui mais moi je veux faire combattre un éclaireur avec un archer, alors ? yell Un peu de calme, on y arrive. wink

   Et bien nous allons tâcher de lever les ambiguïtés de ce projet et pallier à ses manquements. Je pense que vous avez déjà une petite idée de la solution qu'il nous faut adopter. Nous allons dériver les classes Eclaireur et Archer d'une classe de base commune, et cette classe vous la connaissez, ce sera la classe Personnage.

   Nous allons, avec deux nouveaux exemples, montrer l'héritage d'une classe de base commune qui utilise d'une part deux copies d'une classe de base et d'autre part une seule copie par héritage virtuel.

   En attendant, voici les résultats de notre projet obtenus dans la console :

 


    

   1.2 – Héritage d'une classe de base commune


   Cet exemple utilise deux copies d'une classe de base :

 

 

 

   Projet 164HeritageMultiple_3

       Fichier : Personnage.h

 

//Projet 164HeritageMultiple_3
//Héritage d'une classe de base commune
//Fichier : Personnage.h
#include <string>
 
using uint = unsigned int;
 
 
class Personnage
{
public:
 
//constructeurs
Personnage() {}
Personnage(std::string nom) { _nom = nom; }
Personnage(std::string nom, std::string race) { _nom = nom, _race = race; }
 
//Destructeur
virtual ~Personnage() {}
 
//Méthodes membres d'accès publique
std::string get_Name() { return _nom; }
std::string get_Race() { return _race; }
uint get_Life() { return _qte_Vie; }
 
//Autres méthodes
virtual void Se_Presenter() const
{
std::cout << "Je suis un personnage, l'Ancetre de la Cite !" << std::endl;
}
 
virtual void Courir() const { std::cout << "Je cours !" << std::endl; }
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void frapperAvecLesPoings(Personnage &autre) const
{
autre.recevoirDegats(2);
}
 
protected:
 
std::string _nom;
std::string _race;
uint _qte_Vie = 100;
 
}; 

 

   Voilà notre classe de base Personnage. Nous avons supprimé le constructeur surchargé prenant une arme en paramètre, nous supposerons que notre personnage n'a que ses poings pour se défendre. tongue-out

   La méthode membre d'accès publique std::string get_Arm() n'est donc également plus nécessaire. wink

   Pour ce qui est des autres méthodes, notre personnage peut bien entendu se présenter, courir, recevoir des dégâts et comme nous venons de le signaler, frapper avec ses poings.

 

   Fichier : Eclaireur.h

 

//Projet 164HeritageMultiple_3
//Héritage d'une classe de base commune
//Fichier : Eclaireur.h
 
#include <iostream>
#include <string>
 
using uint = unsigned int;
 
class Eclaireur : public Personnage
{
public:
 
//constructeurs
Eclaireur() { Personnage(); }
Eclaireur(std::string nom) { _nom = nom, Personnage(nom); }
Eclaireur(std::string nom, std::string race) { _nom = nom, _race = race,
Personnage(nom, race); }
Eclaireur(std::string nom, std::string race, std::string arme)
{ _nom = nom, _race = race, _arme = arme, Personnage(nom, race); }
 
//Destructeur
virtual ~Eclaireur() {}
 
//Méthodes membres d'accès publique
std::string get_Name() { return _nom; }
std::string get_Race() { return _race; }
std::string get_Arm() { return _arme; }
uint get_Life() { return _qte_Vie; }
 
//Autres méthodes
virtual void Se_Presenter() const
 
{
std::cout << "Je suis un eclaireur a cheval, j'emmene la troupe !" << std::endl;
}
 
void Galoper() const { std::cout << "Je galope !" << std::endl; }
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void frapperAvecLesPoings(Personnage &autre) const
{
autre.recevoirDegats(2);
}
 
void coup_d_Epee(Personnage &autre) const
{
autre.recevoirDegats(10);
}
 
 
protected:
 
std::string _nom;
std::string _race;
std::string _arme;
uint _qte_Vie = 100;
 
}; 

 

      Notre classe Eclaireur hérite de Personnage avec l'accès publique. A la création d'un objet Eclaireur, un constructeur de Personnage est d'abord appelé, celui-ci appelant par la suite un constructeur d'Eclaireur.

   Dans le projet 163HeritageMultiple_2 nous passions une référence à un objet Eclaireur aux méthodes frapperAvecLesPoings() et coup_d_Epee(). Ici, nous passerons une référence à un objet Personnage à ces deux fonctions. En effet, nous voulons qu'un éclaireur puisse également en découdre avec un personnage. laughing Bien, on passe au fichier suivant.


      Fichier : Archer.h 

  

//Projet 164HeritageMultiple_3
//Héritage d'une classe de base commune
//Fichier : Archer.h
 
#include <string>
 
using uint = unsigned int;
 
class Archer : public Personnage
{
public:
 
//constructeurs
Archer() { Personnage(); }
Archer(std::string nom) { _nom = nom, Personnage(nom); }
Archer(std::string nom, std::string race) { _nom = nom, _race = race, Personnage(nomrace); }
Archer(std::string nom, std::string race, std::string arme) { _nom = nom, _race = race, _arme = arme, Personnage(nom, race); }
 
//Destructeur
virtual ~Archer() {}
 
//Méthodes membres d'accès publique
std::string get_Name() { return _nom; }
std::string get_Race() { return _race; }
std::string get_Arm() { return _arme; }
uint get_Life() { return _qte_Vie; }
 
 
//Autres méthodes
 
virtual void Se_Presenter() const
{
std::cout << "Je suis un archer, je defends la cite !" << std::endl;
}
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void Tir_a_l_arc(Personnage &autre) const
{
autre.recevoirDegats(12);
}
 
 
protected:
 
std::string _nom;
std::string _race;
std::string _arme;
uint _qte_Vie = 100;
 
}; 

 

     Notre classe Archer hérite de Personnage avec l'accès publique. A la création d'un objet Archer, un constructeur de Personnage est d'abord appelé, celui-ci appelant par la suite un constructeur d'Archer.

   Dans la classe Archer nous ne redéfinissons pas les méthodes courir() et frapperAvecLesPoings() car nous savons qu'un objet Archer en hérite de Personnage.

   Nous passons également une référence à un objet Personnage à la méthode Tir_a_l_Arc(). En effet, nous voulons qu'un archer puisse également en découdre avec un personnage de la classe de base. wink


      Fichier : Archer_de_cavalerie.h    

 

//Projet 164HeritageMultiple_3
//Héritage d'une classe de base commune
//Fichier : Archer de cavalerie.h
 
#include <iostream>
#include <string>
//#include "Eclaireur.h"
//#include "Archer.h"
 
using uint = unsigned int;
 
 
class Archer_de_cavalerie : public Eclaireur, public Archer
{
public:
 
//constructeurs
Archer_de_cavalerie() { Eclaireur(), Archer(); }
Archer_de_cavalerie(std::string nom) { _nom = nom, Eclaireur(nom), Archer(nom); }
Archer_de_cavalerie(std::string nom, std::string race) { _nom = nom, _race = raceEclaireur(nom, race), Archer(nom, race); }
 
Archer_de_cavalerie(std::string nom, std::string race, std::string arme)
{
_nom = nom, _race = race, _arme = arme, Eclaireur(nom, race, arme), Archer(nom, race, arme);
}
 
//Destructeur
virtual ~Archer_de_cavalerie() {}
 
//Méthodes membres d'accès publique
std::string get_Name() { return _nom; }
std::string get_Race() { return _race; }
std::string get_Arm() { return _arme; }
uint get_Life() { return _qte_Vie; }
 
//Autres méthodes
virtual void Se_Presenter() const override
{
std::cout << "Je suis un archer a cheval, je me deplace rapidement !" << std::endl;
}
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void Tir_a_l_arc(Personnage &autre) const
{
autre.recevoirDegats(14);
}
 
protected:
 
std::string _nom;
std::string _race;
std::string _arme;
uint _qte_Vie = 100;
 
}; 

 

     Voici notre classe Archer_de_cavalerie. Comme dans le précédent projet, celle-ci hérite des classes Eclaireur et Archer. A la création d'un objet Archer_de_cavalerie, les constructeurs de Archer et Eclaireur sont d'abord appelés avant que soit appelé le constructeur d'Archer_de_cavalerie.

   Dans cette classe, bien que la méthode Tir_a_l_Arc() puisse être héritée de la classe Archer, nous la gardons pour l'instant car la quantité de dégâts occasionnés par un archer de cavalerie (14) sont supérieurs aux dégâts que peut infliger un simple archer (12). Nous verrons bientôt comment résoudre ce petit problème. wink

   Voilà, je crois que nous allons passer à la fonction main() mais celle-ci va nous révéler quelques surprises. tongue-out


      Fichier : HeritageMultiple_3.cpp 

 

//Projet 163HeritageMultiple_3
//Constructeurs et méthodes membres surchargés dans l'héritage multiple
//Fichier : HeritageMultiple_3.cpp
 
#include <iostream>
#include <conio.h>
#include "Personnage.h"
#include "Eclaireur.h"
#include "Archer.h"
#include "Archer_de_cavalerie.h"
 
 
using namespace std;
 
int main()
{
 
cout << endl;
cout << "Creation des objets a l'aide des differents constructeurs surcharges" << endl << endl;
 
Personnage ancetre("Enoch");
 
Eclaireur eclaireur("Raniek", "orc", "Epee courte");
 
Archer archer1("Elwyn", "elfe", "arc long");
Archer archer2("Thiellawen", "elfe", "arc long");
 
Archer_de_cavalerie cavalier("Hubert", "humain", "arc composite");
 
cout << "Un personnage se presente :" << endl;
 
cout << "Mon nom est " << ancetre.get_Name() << " ";
ancetre.Se_Presenter();
cout << endl;
 
cout << "Les pdV initiaux de chaque combattant sont de 100" << endl;
cout << eclaireur.get_Name() << " est un eclaireur " << eclaireur.get_Race() << endl;
cout << "Il est arme d'une " << eclaireur.get_Arm() << " (-10 pdV)" << endl;
eclaireur.Se_Presenter();
cout << endl;
cout << archer1.get_Name() << " est un archer " << archer1.get_Race() << endl;
cout << "Il est arme d'un " << archer1.get_Arm() << " (-12 pdV)" << endl;
archer1.Se_Presenter();
cout << endl;
cout << archer2.get_Name() << " est une archere " << archer2.get_Race() << endl;
cout << "Elle est armee d'un " << archer2.get_Arm() << " (-12 pdV)" << endl;
archer2.Se_Presenter();
cout << endl;
cout << cavalier.get_Name() << " est un archer de cavalerie " << cavalier.get_Race() << endl;
cout << "Il est arme d'un " << cavalier.get_Arm() << " (-14 pdV)" << endl;
cavalier.Se_Presenter();
cout << endl;
cout << "Quelques echanges de coups..." << endl;
 
//cout << "En embuscade, les elfes Elwyn et Thiellawen decochent chacun une fleche";
//cout << endl;
//cout << "vers l'archer de cavalerie Hubert qui se dirige vers eux (- 2*12 pdV)";
//cout << endl;
//archer1.Tir_a_l_arc(cavalier);
//archer2.Tir_a_l_arc(cavalier);
 
cout << "Les pdV d'Hubert sont desormais de " << cavalier.get_Life() << endl << endl;
cout << "Hubert est blesse mais il se ressaisit et tire a son tour sur Elwyn (- 14 pdV)" << endl;
 
cavalier.Tir_a_l_arc(archer1);
cout << "Les pdV d'Elwyn sont maintenant de " << archer1.get_Life() << endl << endl;
 
cout << "Profitant de la confusion, Raniek s'est avance et donne un coup d'epee a Hubert (- 10 pdV)" << endl;
 
//eclaireur.coup_d_Epee(cavalier);
 
cout << "Les pdV d'Hubert sont maintenant de " << cavalier.get_Life() << endl << endl;
 
cout << "Surpris, Hubert peut neanmoins decocher une fleche a Raniek (-14 pdV)";
cout << endl;
cavalier.Tir_a_l_arc(eclaireur);
cout << "Les pdV de Raniek sont desormais de " << eclaireur.get_Life() << endl;
cout << endl;
 
cout << "Se sentant depasse par le nombre, Hubert s'enfuit..." << endl;
cavalier.Galoper();
 
_getch();
return 0;
} 

 

     Et voici notre fonction main(). Pourquoi ai-je mis plusieurs lignes de code en remarque ? surprised

   Et bien, les trois lignes suivantes provoquent toutes une certaine irritation du compilateur money-mouth :

 

//archer1.Tir_a_l_arc(cavalier);
//archer2.Tir_a_l_arc(cavalier);
//eclaireur.coup_d_Epee(cavalier);
 
Erreur : conversion ambiguë d'Archer_de_cavalerie vers Personnage ! 

 

     Souvenez-vous que nous venons de modifier l'objet passé à quelques méthodes en passant chaque fois un personnage en paramètre en nous disant qu'un personnage pouvait, lui aussi participer au combat s'il le désirait, ce qui était tout à fait notre droit. Alors pourquoi le compilateur nous embête-t-il en signalant cette erreur ? surprised

   Il se fait que dans ce cas, le compilateur ne comprend pas pourquoi on lui passe un objet Archer_de_cavalerie en paramètre. D'une part, la classe Personnage ne possède pas les méthodes coup_d_Epee() et Tir_a_l_Arc() et d'autre part celles-ci sont appelées par des objets issus des classes de base de la classe Archer_de_cavalerie, ces fonctions attendant un personnage en paramètre alors que nous leur passons un cavalier !? surprised

   La solution est toute simple, l'héritage virtuel va nous venir en aide, nous allons voir comment. wink

 


   2.0 – L'Héritage virtuel

 

   Pour résoudre notre problème, la classe Personnage doit être une classe de base virtuelle des classes Eclaireur et Archer. Il suffit que le mot-clé ''virtual'' soit ajouté dans les déclarations des deux classes dérivées de Personnage

 

class Eclaireur : virtual public Personnage
class Archer : virtual public Personnage 

 

     Nous savons que lors d'un héritage simple, la construction d'un objet d'une classe dérivée se fait premièrement par l'appel du constructeur de sa classe de base et seulement ensuite par l'appel du constructeur de sa propre classe.

   Dans le cas de classes de base héritées virtuellement, c'est le contraire qui se produit. Ces classes sont initialisées par leur classe dérivée la plus basse dans leur arbre d'héritage. Cela veut dire que la classe Personnage sera initialisée par Archer_de_cavalerie et non par Eclaireur et Archer. Et, bien que les constructeurs de ces deux classes doivent initialiser la classe Personnage, cette opération sera ignorée lors de la création d'un objet Archer_de_cavalerie. Voilà, nous pouvons maintenant passer un objet Archer_de_cavalerie à une méthode des deux classes de base qui attend un personnage en paramètre. wink

   Nous allons présenter un nouvel exemple dans lequel nous expliquerons les modifications nécessaires. laughing

 

 

    Cet exemple utilise une seule copie de la classe Personnage par héritage virtuel.

   Projet 165HeritageVirtuel_1

      Fichier : Personnage.h

 

//Projet 165HeritageVirtuel_1
//Héritage virtuel d'une classe de base commune
//Fichier : Personnage.h
 
#include <string>
 
using uint = unsigned int;
 
 
class Personnage
{
public:
 
//constructeurs
Personnage() {}
Personnage(std::string nom) { _nom = nom; }
Personnage(std::string nom, std::string race) { _nom = nom, _race = race; }
 
//Destructeur
virtual ~Personnage() {}
 
//Méthodes membres d'accès publique
std::string get_Name() const { return _nom; }
std::string get_Race() const { return _race; }
uint get_Life() const { return _qte_Vie; }
 
//Autres méthodes
virtual void Se_Presenter() const
{
std::cout << "Je suis un personnage, l'Ancetre de la Cite !" << std::endl;
}
 
void Courir() const { std::cout << "Je cours !" << std::endl; }
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void frapperAvecLesPoings(Personnage &autre) const
{
autre.recevoirDegats(2);
}
 
 
protected:
 
std::string _nom;
std::string _race;
uint _qte_Vie = 100;
 
}; 

 

   La classe Personnage ne présente pas de particularités, nous gardons nos trois méthodes membres ainsi que nos trois variables de type ''protected''.

   Remarque : La classe Personnage étant virtuelle, nous devons le signaler dans ses deux classes dérivées Eclaireur et Archerwink

 

   Fichier : Eclaireur.h

 

//Projet 165HeritageVirtuel_1
//Héritage virtuel d'une classe de base commune
//Fichier : Eclaireur.h
#include <iostream>
#include <string>
 
using uint = unsigned int;
 
class Eclaireur : virtual public Personnage
{
public:
 
//constructeurs
Eclaireur() { Personnage(); }
Eclaireur(std::string nom) { _nom = nom, Personnage(nom); }
Eclaireur(std::string nom, std::string race) { _nom = nom, _race = racePersonnage(nom, race); }
Eclaireur(std::string nom, std::string race, std::string arme{ _nom = nom, _race = race, _arme = arme, Personnage(nom, race); }
 
//Destructeur
virtual ~Eclaireur() {}
 
//Méthode membre d'accès publique
std::string get_Arm() const { return _arme; }
 
//Autres méthodes
virtual void Se_Presenter() const override
{
std::cout << "Je suis un eclaireur a cheval, j'emmene la troupe !" << std::endl;
}
 
void Galoper() const { std::cout << "Je galope !" << std::endl; }
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void frapperAvecLesPoings(Personnage &autre) const
{
autre.recevoirDegats(2);
}
 
void coup_d_Epee(Personnage &autre) const
{
autre.recevoirDegats(10);
}
 
 
protected:
 
std::string _arme;
 
 
}; 

 

     La classe Eclaireur hérite de la classe virtuelle Personnage. Remarquez que les variables membres std::string _nom, std::string _race et uint _qte_Vie sont héritées de leur classe de base ainsi que les méthodes std::string get_Name(), std::string get_Race et uint get_Life().


      Fichier : Archer.h

 

//Projet 164HeritageMultiple_3
//Héritage d'une classe de base commune
//Fichier : Archer.h
 
#include <string>
 
using uint = unsigned int;
 
class Archer : virtual public Personnage
{
public:
 
//constructeurs
Archer() { Personnage(); }
Archer(std::string nom) { _nom = nom, Personnage(nom); }
Archer(std::string nom, std::string race) { _nom = nom, _race = race, Personnage(nomrace); }
Archer(std::string nom, std::string race, std::string arme) { _nom = nom, _race = race, _arme = arme, Personnage(nom, race); }
 
//Destructeur
virtual ~Archer() {}
 
//Méthodes membres d'accès publique
std::string get_Arm() const { return _arme; }
 
//Autres méthodes
virtual void Se_Presenter() const override
{
std::cout << "Je suis un archer, je defends la cite !" << std::endl;
}
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void Tir_a_l_arc(Personnage &autre) const
{
autre.recevoirDegats(12);
}
 
protected:
 
std::string _arme;
 
 
}; 

 

    La classe Archer hérite de la classe virtuelle Personnage. Remarquez qu'ici aussi les variables membres std::string _nom, std::string _race et uint _qte_Vie sont héritées de leur classe de base ainsi que les méthodes std::string get_Name(), std::string get_Race et uint get_Life().


      Fichier : Archer_de_cavalerie.h

 

//Projet 165HeritageVirtuel_1
//Héritage virtuel d'une classe de base commune
//Fichier : Archer_de_cavalerie.h
 
#include <iostream>
#include <string>
//#include "Eclaireur.h"
//#include "Archer.h"
 
using uint = unsigned int;
 
 
class Archer_de_cavalerie : public Eclaireur, public Archer
{
public:
 
//constructeurs
Archer_de_cavalerie() { Personnage(); }
Archer_de_cavalerie(std::string nom) { _nom = nom, Personnage(nom); }
Archer_de_cavalerie(std::string nom, std::string race) {
_nom = nom, _race = race, Personnage(nom, race);
}
 
Archer_de_cavalerie(std::string nom, std::string race, std::string arme)
{
_nom = nom, _race = race, _arme = arme, Eclaireur(nom, race, arme), Archer(nom, race, arme);
}
 
//Destructeur
virtual ~Archer_de_cavalerie() {}
 
//Méthode membre d'accès publique
std::string get_Arm() const { return _arme; }
 
//Autres méthodes
virtual void Se_Presenter() const final
{
std::cout << "Je suis un archer a cheval, je me deplace rapidement !";
std::cout << std::endl;
}
 
virtual void recevoirDegats(uint degats) { _qte_Vie -= degats; }
 
void Tir_a_l_arc(Personnage &autre) const
{
autre.recevoirDegats(14);
}
 
protected:
 
std::string _arme;
 
}; 

 

    La classe Archer_de_cavalerie hérite des classes Eclaireur et Archer. Les variables membres std::string _nom, std::string _race et uint _qte_Vie sont héritées de leur classe de base ainsi que les méthodes std::string get_Name(), std::string get_Race et uint get_Life().

   Remarquez également que la méthode Se_Presenter() a été déclarée ''final'' (non obligatoire), car elle se trouve être dans la classe la plus basse dans la hiérarchie des classes de notre programme et ne sera donc pas redéfinie dans une classe dérivée. wink


      Fichier : HeritageVirtuel_1.cpp

   Dans la fonction main() nous pouvons maintenant, comme nous en avions parlé précédemment, faire également combattre ensemble un éclaireur et un archer, nous le montrerons à la fin de la fonction. wink

 

 

//Projet 165HeritageVirtuel_1
//Héritage virtuel d'une classe de base commune
//Fichier : HeritageVirtuel_1.cpp
 
#include <iostream>
#include <conio.h>
#include "Personnage.h"
#include "Eclaireur.h"
#include "Archer.h"
#include "Archer_de_cavalerie.h"
 
 
using namespace std;
 
int main()
{
 
cout << endl;
cout << "Creation des objets a l'aide des differents constructeurs surcharges";
cout << endl << endl;
 
Personnage ancetre("Enoch");
 
Eclaireur eclaireur("Raniek", "orc", "Epee courte");
 
Archer archer1("Elwyn", "elfe", "arc long");
Archer archer2("Thiellawen", "elfe", "arc long");
 
Archer_de_cavalerie cavalier("Hubert", "humain", "arc composite");
 
cout << "Un personnage se presente :" << endl;
cout << "Mon nom est " << ancetre.get_Name() << " ";
ancetre.Se_Presenter();
cout << endl;
 
cout << "Les pdV initiaux de chaque combattant sont de 100" << endl;
cout << eclaireur.get_Name() << " est un eclaireur " << eclaireur.get_Race() << endl;
cout << "Il est arme d'une " << eclaireur.get_Arm() << " (-10 pdV)" << endl;
eclaireur.Se_Presenter();
cout << endl;
cout << archer1.get_Name() << " est un archer " << archer1.get_Race() << endl;
cout << "Il est arme d'un " << archer1.get_Arm() << " (-12 pdV)" << endl;
archer1.Se_Presenter();
cout << endl;
cout << archer2.get_Name() << " est une archere " << archer2.get_Race() << endl;
cout << "Elle est armee d'un " << archer2.get_Arm() << " (-12 pdV)" << endl;
archer2.Se_Presenter();
cout << endl;
cout << cavalier.get_Name() << " est un archer de cavalerie " << cavalier.get_Race();
cout << endl;
 
cout << "Il est arme d'un " << cavalier.get_Arm() << " (-14 pdV)" << endl;
cavalier.Se_Presenter();
cout << endl;
cout << "Quelques echanges de coups..." << endl;
 
cout << "En embuscade, les elfes Elwyn et Thiellawen decochent chacun une fleche ";
cout << endl;
cout << "vers l'archer de cavalerie Hubert qui se dirige vers eux (- 2*12 pdV)";
cout << endl;
archer1.Tir_a_l_arc(cavalier);
archer2.Tir_a_l_arc(cavalier);
cout << "Les pdV d'Hubert sont desormais de " << cavalier.get_Life() << endl << endl;
cout << "Hubert est blesse mais il se ressaisit et tire a son tour sur Elwyn (- 14 pdV)" << endl;
cavalier.Tir_a_l_arc(archer1);
cout << "Les pdV d'Elwyn sont maintenant de " << archer1.get_Life() << endl << endl;
 
cout << "Profitant de la confusion, Raniek s'est avance et donne un coup d'epee a Hubert (- 10 pdV)" << endl;
eclaireur.coup_d_Epee(cavalier);
cout << "Les pdV d'Hubert sont maintenant de " << cavalier.get_Life() << endl;
cout << endl;
 
cout << "Surpris, Hubert peut neanmoins decocher une fleche a Raniek (-14 pdV)";
cout << endl;
cavalier.Tir_a_l_arc(eclaireur);
cout << "Les pdV de Raniek sont desormais de " << eclaireur.get_Life() << endl;
cout << endl;
 
cout << "Se sentant depasse par le nombre, Hubert s'enfuit..." << endl;
cavalier.Galoper();
cout << endl;
cout << "Maintenant nous faisons combattre un eclaireur et un archer" << endl;
cout << endl;
cout << "L'archere elfe " << archer2.get_Name() << " decoche une fleche a l'eclaireur orc ";
cout << eclaireur.get_Name() << " (-12 pdV)" << endl;
cout << archer1.get_Name() << " tire a son tour vers " << eclaireur.get_Name();
cout << " (-12 pdV)";
archer2.Tir_a_l_arc(eclaireur);
archer1.Tir_a_l_arc(eclaireur);
cout << endl;
cout << "Les pdV de Raniek sont desormais de " << eclaireur.get_Life() << endl;
cout << endl;
cout << eclaireur.get_Name() << " a juste le temps de frapper ";
cout << archer1.get_Name();
cout << " d'un coup d'epee (-10 pdV)" << endl;
eclaireur.coup_d_Epee(archer1);
cout << "Les pdV d'Elwyn sont maintenant de " << archer1.get_Life() << endl << endl;
cout << "Raniek s'enfuit..." << endl;
eclaireur.Galoper();
 
_getch();
return 0;
} 

 

   Voilà. Vous pouvez maintenant aussi faire combattre un personnage avec n'importe quel autre combattant des autres classes, la classe personnage étant déclarée virtuelle, ces combats vous sont permis dans cet exemple.

   Et tout ceci nous donne dans la console :

 

 

 

  3. Corrigé des exercices du chapitre 27

 

     Exercice 1

   Pour cet exercice on demandait : Réécrivez le projet 158HeritageSimple_11 en ajoutant les pointeurs vers les objets dans un tableau que vous déclarerez au début de la fonction main().

   Vous afficherez les fonctions Se_Presenter() à l'aide des indices du tableau.

   Nous n'avons donc besoin que de réécrire la fonction main(). Voici cette fonction modifiée.

   Fichier : Chap27Ex_1_1.cpp

 

 

int main()
{
 
Personnage unPersonnage;
Personnage *ptr = nullptr;
char choix('1');
int i(0);
 
//On crée un pointeur sur un tableau de 5 éléments
Personnage *tab[5];
 
std::cout << std::endl;
std::cout << "Resolution statique des liens : appel de la fonction Presentation()";
std::cout << std::endl;
 
std::cout << "La liaison de l'objet a la fonction Presentation() se fait des la compilation : " << std::endl;
 
Presentation(unPersonnage);
 
std::cout << std::endl;
while (choix == '1' || choix == '2' || choix == '3' || choix == '4')
{
std::cout << "(1) Personnage (2) Guerrier (3) Archer (4) Druide";
std::cout << std::endl;
std::cout << "Pressez un autre caractere pour Quitter" << std::endl;
std::cout << "Votre choix : ";
std::cin >> choix;
 
switch (choix)
{
case '1':
ptr = new Personnage;
break;
 
case '2':
ptr = new Guerrier;
break;
 
case '3':
ptr = new Archer;
break;
 
case '4':
ptr = new Druide;
break;
 
default:
ptr = new Personnage;
break;
}
 
std::cout << std::endl;
tab[i] = ptr;
++i;
 
}
 
 
std::cout << "Resolution dynamique des liens : se fait pendant le deroulement du programme" << std::endl;
std::cout << "Appel de la fonction Se_Presenter() selon l'indice du tableau tab";
std::cout << std::endl;
 
for (int i(0); i != 5; ++i)
tab[i]->Se_Presenter();
 
delete ptr;
 
_getch();
return 0;
} 

 

   Au début de notre fonction main() nous créons un pointeur sur un tableau ''tab'' de 5 éléments ''Personnage''.

   Dans notre boucle while, chaque fois qu'un choix est effectué, le pointeur pointant vers un objet d'une de nos classes est ajouté dans le tableau.

   A la sortie du while, nous bouclons à travers le tableau pour afficher les différentes méthodes virtuelles Se_presenter(). Rien de compliqué ici donc, avec comme résultat dans la console :

 

 

 

   Exercice 2 (un petit challenge)

   Pour cet exercice on demandait de réécrire le projet 162HeritageMultiple_1 en utilisant des références à la place de pointeurs.

   Peut-être était-il utile à cet effet de revoir le paragraphe 1.3 du chapitre 9 concernant la ''Création et destruction de références dans le tas'' mais vous pouvez toujours y jeter un coup d'oeil si besoin en est ? wink

   Le code :

      Fichier : Chap27Ex_2_1.cpp

   Ici aussi nous n'avions besoin que de réécrire la fonction main() dont voici la modification.   

 

int main()
{
 
Eclaireur &ref = *new Eclaireur;
Archer &ref2 = *new Archer;
 
Archer_de_cavalerie &ref3 = *new Archer_de_cavalerie;
 
int i;
char choix('1');
 
cout << endl;
 
while (choix == '1' || choix == '2' || choix == '3')
{
cout << "(1) Eclaireur" << endl;
cout << "(2) Archer" << endl;
cout << "(3) Archer de cavalerie" << endl;
cout << "Pressez un autre caractere pour quitter" << endl << endl;
cout << "Votre choix : ";
cin >> choix;
 
switch (choix)
{
 
case '1':
ref = *new Eclaireur;
ref.Se_Presenter();
ref.Galoper();
cout << endl;
break;
 
case '2':
ref2 = *new Archer;
ref2.Se_Presenter();
ref2.Tir_a_l_arc();
cout << endl;
break;
 
case '3':
ref3 = *new Archer_de_cavalerie;
ref3.Se_Presenter();
ref3.Tir_a_l_arc2();
ref3.Galoper();
cout << endl;
break;
 
default:
 ref3 = *new Archer_de_cavalerie;
break;
}
 
}
 
delete (Eclaireur*)(&ref);
delete (Archer*)(&ref2);
delete (Archer_de_cavalerie*)(&ref3);
 
_getch();
return 0;
} 

 

    Pas besoin d'autres explications ici donc, vous trouverez tout ce qu'il faut savoir dans le chapitre 9.

   Et le résultat dans la console est identique à celui du projet 162HeritageMultiple_1.


   Voilà, c'est tout en ce qui concerne ce chapitre.

   La première partie de notre big tuto C++ se termine avec ce chapitre. J'espère que ces tutoriels vous auront permis d'apprendre (ou de vous rappeler) certaines notions et astuces du C++.

   J'aimerais que ce big tuto ait cependant une suite car nous sommes loin d'avoir vu tout ce que la puissance de ce langage a encore à nous apporter.

   Il me faudra néanmoins un certain temps pour en préparer la suite car je me dirige vers d'autres occupations pour l'instant. En tout cas, j'espère que ces vingt-huit chapitres vous auront aidé à progresser dans ce riche et relativement difficile langage.                                                          

            Gondulzak.  angel
 
 
 
 

Connexion

CoalaWeb Traffic

Today35
Yesterday282
This week829
This month3122
Total1742329

19/04/24