PROFDINFO.COM

Votre enseignant d'informatique en ligne

Chapitre 5 - Le polymorphisme

La classe inversée

Lors du chapitre 5, nous allons expérimenter une nouvelle approche pédagogique qui se nomme la classe inversée. La classe inversée consiste à apprendre individuellement la théorie avant d'arriver en classe (souvent à l'aide d'un enregistrement vidéo de votre professeur) puis de faire des exercices sur cette théorie en classe. La classe inversée comporte beaucoup d'avantages:

Par contre, le succès de cette approche repose sur votre participation! Vous n'aurez pas plus de travail qu'à l'habitude puisque le nombre d'heures de travail personnel à la maison (4 heures par semaine pour le cours de KAB) sera respecté. Vous devez seulement faire le travail avant de vous présenter en classe plutôt qu'après!

Objectifs d'apprentissage

Avant le cours

Pendant le cours

Consignes

Les étapes 5.1 et 5.2 devront donc être faites à la maison avant le cours du vendredi 7 avril 2017.

Les étapes 5.3 et 5.4 seront faites durant les cours du 7 et du 10 avril 2017.

L'étape 5.5 sera faite les cours suivants.

Les étapes 5.1 à 5.4 ne seront pas évaluées sommativement. Par contre, ils sont nécessaires à la compréhension du chapitre 5. L'étape 5.5 est un exercice individuel d'application du polymorphisme et il sera évalué.

Étape 5.1 - La capsule vidéo et le schéma de concepts

Téléchargez le document Word Polymorphisme - Etudiants.docx, enregistrez-le dans votre répertoire de travail et ouvrez-le.

Tout en complétant ce document Word, écoutez la capsule vidéo suivante:

Chapitre 5 - Le polymorphisme

Sous une forme papier ou électronique, le document Word devra être présenté à votre professeur au début du cours.

Étape 5.2 Exercice d'auto-évaluation: les véhicules

Voici la représentation UML de quatre classes CVehicule,  CAvion , CAutomobile et CDecapotable:

vehicules uml
Voici le code C++ correspondant :

class CVehicule  
{
	float VitesseMax_ ;
public:
	CVehicule();   
	CVehicule(float VitesseMax);
	~CVehicule();
	float Deplacer(float Distance);
};

class CAvion : public CVehicule
{
	float AltitudeMax_;
public:
	CAvion();
	CAvion(float VitesseMax, float AltitudeMax);
	~CAvion();
	float Deplacer(float Distance);
	void Decoller();
};

class CAutomobile : public CVehicule
{

	int NbRoues_;
public:
	CAutomobile();
	CAutomobile(float VitesseMax, int NbRoues);
	float Deplacer(float Distance);
	void ChangerRoue();
	~CAutomobile();
};

class CDecapotable : public CAutomobile  
{
	float GrandeurToit_;
public:
	CDecapotable() ;
	CDecapotable(float VitesseMax, int NbRoues, 
                 float GrandeurToit) ;
	~CDecapotable();
	float Deplacer(float Distance);
	void OuvrirToit();
};

Parmi les lignes suivantes, quelles sont les lignes qui ne compileront pas? Pour chaque ligne qui ne compile pas, expliquez-vous raisonnement.

  1.     CDecapotable* pObj1 = new CDecapotable;
        CDecapotable* pObj2 = new CAutomobile;
        CDecapotable* pObj3 = new CAvion; 
        CDecapotable* pObj4 = new CVehicule; 
        
        CAutomobile* pObj5 = new CDecapotable;
        CAutomobile* pObj6 = new CAutomobile;
        CAutomobile* pObj7 = new CAvion;
        CAutomobile* pObj8 = new CVehicule; 
        
        CAvion* pObj9 = new CDecapotable;
        CAvion* pObj10 = new CAutomobile;
        CAvion* pObj11 = new CAvion;
        CAvion* pObj12 = new CVehicule; 
        
        CVehicule* pObj13 = new CDecapotable;
        CVehicule* pObj14 = new CAutomobile;
        CVehicule* pObj15 = new CAvion;
        CVehicule* pObj16 = new CVehicule; 
        
    }         
  2. Pour chacun des appels suivants à la méthode Déplacer(), déterminez la version de la méthode qui est utilisée (CVehicule,  CAvion, CAutomobile ou CDecapotable):

        pObj1->Deplacer(1.0f);
        
        pObj5->Deplacer(1.0f);    
        pObj6->Deplacer(1.0f);    
        
        pObj11->Deplacer(1.0f);    
        
        pObj13->Deplacer(1.0f);    
        pObj14->Deplacer(1.0f);    
        pObj15->Deplacer(1.0f);    
        pObj16->Deplacer(1.0f);    
               
  3. Le mot-clé virtual est ajouté devant toutes les déclarations des méthodes Deplacer(). Déterminez de nouveau la version de la méthode qui est utilisée (CVehicule,  CAvion, CAutomobile ou CDecapotable).

SOLUTION

Voici les solutions à l'exercice 5.2. Afin d'en tirer le maximum de bénéfices, vous devez dans un premier temps réaliser l'exercice 5.2 sans regarder la solution! Gardez une trace de votre solution car vous devrez la présenter à votre enseignant au début du cours. Ensuite, vous pouvez vous auto-corriger avec la solution ci-dessous afin de vérifier votre compréhension de la matière.

  1.     pObj2: l'automobile n'est pas nécessairement une décapotable
        pObj3: l'avion n'est pas une décapotable 
        pObj4: le véhicule n'est pas nécessairement une décapotable 
    
        
        pObj7: l'avion n'est pas une automobile
        pObj8: le véhicule n'est pas nécessairement une automobile 
        
        pObj9: la décapotable n'est pas un avion
        pObj10: l'automobile n'est pas un avion  
        pObj12: le véhicule n'est pas nécessairement un avion 
    

  2.     pObj1: CDecapotable
        
        pObj5: CAutomobile  
        pObj6: CAutomobile  
        
        pObj11: CAvion
        
        pObj13: CVehicule
        pObj14: CVehicule
        pObj15: CVehicule
        pObj16: CVehicule
               

  3.     pObj1: CDecapotable
        
        pObj5: CDecapotable  
        pObj6: CAutomobile  
        
        pObj11: CAvion
        
        pObj13: CDecapotable
        pObj14: CAutomobile
        pObj15: CAvion
        pObj16: CVehicule           

5.3 Questions de sondage

En classe

5.4 Exercices en équipes

Consignes

Dans un premier temps, placez-vous en équipe de deux étudiants. Le professeur vous assignera un exercice à réaliser en équipe de deux parmi les exercices A à E ci-dessous.

Lorsque vous aurez terminé l'exercice qui vous a été assigné, faites les exercices F et G.

Dans un deuxìème temps, joignez-vous à une autre équipe qui a travaillé sur le même exercice A à E et comparez vos réponses. Utilisez les points forts de chaque réponse afin de créer une seule réponse améliorée.

Exercices

  1. Un jeu vidéo fort populaire offre la possibilité au joueur de créer divers personnages. Il y a le paladin qui attaque avec son épée légendaire et l'archer qui décoche des flèches de feu. Lors de l'exploration d'un dongeon ténébreux, Tanambar le paladin et Noldor l'archer rencontrent un orc et l'attaquent. Il y aura donc au moins trois classes et une méthode Attaquer. En utilisant le polymorphisme:
    1. dessinez le diagramme UML de ces classes;
    2. faites une ébauche des déclarations C++ de ces classes et;
    3. écrivez un programme principal C++ simple qui utilise ces classes
  2. On désire créer une classe CInventaire qui est composée d'un tableau de CArticle (voir votre exercice 3.4). On dérive de CInventaire deux classes qui permettront de gérer différement le tableau d'articles. Une première classe permettra d'ajouter des articles sans vérifier si l'article est déjà dans l'inventaire. Elle permettra donc les doublons. La deuxième classe ajoutera les articles dans l'inventaire en vérifiant d'abord si l'article est déjà présent dans le tableau. S'il est déjà présent dans le tableau, la quantité sera simplement augmentée. Il n'y aura donc pas de doublons. En utilisant le polymorphisme:
    1. dessinez le diagramme UML de ces classes;
    2. faites une ébauche des déclarations C++ de ces classes et;
    3. écrivez un programme principal C++ simple qui utilise ces classes.
  3. Une ferme d'animaux est composée d'un ensemble d'ânes et de chèvres. Pour représenter cette ferme, nous voulons créer un tableau de pointeurs sur des animaux. Suite à un évènement imprévu (disons un orage), tous les animaux se mettent à crier un après l'autre. Les ânes font "hi-an" et les chèvres "bééé". Il y aura donc au moins trois classes et une méthode Crier. En utilisant le polymorphisme:
    1. dessinez le diagramme UML de ces classes;
    2. faites une ébauche des déclarations C++ de ces classes et;
    3. écrivez un programme principal C++ simple qui utilise ces classes
  4. On désire créer deux versions d'une même classe qui est composée d'un tableau d'entiers. La première version permettra d'ajouter des entiers au tableau sans aucun ordre précis tandis que la deuxième version permettra aussi d'ajouter des entiers au tableau, mais en s'assurant que le tableau est toujours trié en ordre croissant. En utilisant le polymorphisme:
    1. dessinez le diagramme UML de ces classes;
    2. faites une ébauche des déclarations C++ de ces classes et;
    3. écrivez un programme principal C++ simple qui utilise ces classes.
  5. Un étudiant veut programmer une classe qui lui permettra de contrôler deux types de robots: un drône et un robot mobile. Chaque robot a aussi deux types de capteurs: un capteur à infrarouges et un capteur à ultrasons. Avant de se déplacer, chaque robot interroge ses deux capteurs. En utilisant le polymorphisme:
    1. dessinez le diagramme UML de ces classes;
    2. faites une ébauche des déclarations C++ de ces classes et;
    3. écrivez un programme principal C++ simple qui utilise ces classes.
  6. Django, un musicien célèbre, possède trois guitares: une guitare bleue, une guitare rouge et une guitare verte. Avec toutes ses guitares il peut jouer son style de musique préférée: le jazz maanouche. Il y aura donc au moins deux classes et une méthode Jouer.
    1. dessinez le diagramme UML de ces classes;
    2. faites une ébauche des déclarations C++ de ces classes et;
    3. écrivez un programme principal C++ simple qui utilise ces classes.
  7. D'après-vous, est-ce que l'extrait de code ci-dessous est une utillisation correcte du polymorphisme? Justifiez votre réponse.

    #include <iostream>
    #include <string>
    using namespace std;
    
    class CAnimal
    {
    public:
    	void Manger(string Nourriture) {
    		cout << "Miam! Un " << Nourriture;
    	}
    	virtual string Type() const { return "Animal"; }
    };
    
    class CChien : public CAnimal
    {
    public:
    	string Type() const { return "Chien"; }
    };
    
    class CChat : public CAnimal
    {
    public:
    	string Type() const { return "Chat"; }
    };
    
    int main()
    {
    	CAnimal* Animal = nullptr;
    	if (rand() % 2 == 0)
    	{
    		Animal = new CChien;
    	}
    	else
    	{
    		Animal = new CChat;
    	}
    	if (Animal->Type() == "Chien") Animal->Manger("steak");
    	if (Animal->Type() == "Chat") Animal->Manger("poisson");
    	
    	delete Animal;
    }

5.5 Exercice d'application personnel en classe - Le retour des oiseaux migrateurs

Le printemps est là et un des signes annonciateurs de la belle saison est le retour des oiseaux migrateurs qui parcourent le vaste ciel. Comme c'est le cas chez plusieurs espèces, il y a des oiseaux meneurs meneur et des oiseaux suiveurs suiveu.

Une fois de plus, copiez votre exercice 3.7 dans un nouveau répertoire et renommez-le Exercice 5.2.

Ajoutez à votre projet la classe COiseau qui doit dériver de la classe CPersonnage et qui contient les membres suivants:

Ajoutez à votre projet la classe CMeneur qui doit dériver de la classe COiseau et qui contient les membres suivants:

Ajoutez à votre projet la classe CSuiveu qui doit dériver de la classe COiseau et qui contient les membres suivants:

De plus, vous devez obligatoirement utiliser le programme principal suivant tout en complétant la fonction TrouverPlusProcheMeneur:

// Exercice5.5.h
      #pragma once

const int Largeur = 576;
const int Hauteur = 576;
const int NbIterations = 10000;
const int NbMeneurs = 5;
const int NbSuiveux = 45;
const int NbOiseaux = NbMeneurs + NbSuiveux;

// main.cpp
#include <SFML/Graphics.hpp>
#include "Exercice5.2.h"
#include "Meneur.h"
#include "Suiveu.h"


///////////////////////////////////////////////////////////////////////////
// TrouverPlusProcheMeneur
// But: la fonction trouve le meneur le plus proche de l'oiseau passé en
//      paramètre. Pour ce faire, la fonction doit tester chacun des
//      oiseaux du tableau. Si l'oiseau est un meneur et que l'oiseau
//      n'est pas le même que l'oiseau courant alors la distance entre
//      les deux oiseaux est calculée. La distance entre deux points 
//      se calcule avec la formule suivante: sqrt( (x1-x2)^2 + (y1-y2)^2 ).
//      La distance minimale ainsi qu'un pointeur vers le meneur sont
//      conservés dans des variables temporaires. Le pointeur vers le meneur
//      le plus proche est retourné.
//      Il est à noter qu'il n'est pas utile de calculer la racine lorsque
//      l'on cherche le minimum. 
//      
// Intrants: OiseauCourant: oiseau dont on veut trouver 
//                          le plus proche meneur
//           tab:  tableau de tous les oiseaux
//           NbOiseaux: taille du tableau
// Extrant:  pointeur vers le meneur le plus proche
///////////////////////////////////////////////////////////////////////////

const COiseau* TrouverPlusProcheMeneur(const COiseau* OiseauCourant, COiseau* Tab[], int NbOiseaux)
{
 

   // Cette ligne permet seulement de compiler le programme principal, mais il vous faudra
   // la modifier lorsque que vous aurez terminé l'implémentation de cette fonction.
   return OiseauCourant; 
}

int main(int argc, char *argv[])
{
   COiseau* Tab[NbOiseaux];

   RenderWindow Fenetre(VideoMode(Largeur, Hauteur), "Le retour des oiseaux migrateurs");
   Texture FondImg;
   if (!FondImg.loadFromFile("ciel.bmp"))
   {
      return EXIT_FAILURE;
   }
   Sprite Fond(FondImg);

   Texture SuiveuImg;
   if (!SuiveuImg.loadFromFile("Suiveu.png"))
   {
      return EXIT_FAILURE;
   }

   Texture MeneurImg;
   if (!MeneurImg.loadFromFile("Meneur.png"))
   {
      return EXIT_FAILURE;
   }

   srand(static_cast<unsigned int>(time(0)));

   for (int i = 0; i < NbSuiveux; i++)
   {
      Tab[i] = new CSuiveu(SuiveuImg, rand() % (Largeur),
         rand() % (Hauteur));
   }
   for (int i = NbSuiveux; i < NbOiseaux; i++)
   {
      Tab[i] = new CMeneur(MeneurImg, rand() % (Largeur),
         rand() % (Hauteur));
   }

   for (int i = 0; i <NbIterations; i++)
   {
      Fenetre.clear();
      Fenetre.draw(Fond);
      for (int i = 0; i < NbOiseaux; i++)
      {
         const COiseau* PlusProche =
            TrouverPlusProcheMeneur(Tab[i], Tab, NbOiseaux);
         Tab[i]->SeDeplacer(PlusProche);
         Tab[i]->Afficher(Fenetre);
      }
      Fenetre.display();
      sleep(milliseconds(10));
   }
   for (int i = 0; i < NbOiseaux; i++)
   {
      delete Tab[i];
   }
   return 0;
}

Il est à noter que la programmation correcte de la méthode TrouverPlusProcheMeneur est un défi. Ne vous laissez pas impressionner par la tâche et demandez l'aide de votre gentil enseignant si vous éprouvez des difficultés. Mieux vaux remettre votre exercice avec la version initiale de cette fonction plutôt que de ne pas remettre votre exercice du tout.

Vos oiseaux peuvent se mouvoir dans le somptueux ciel suivant:

ciel