1 / 88

Chapitre VI

Chapitre VI. Permet à une variable déclarée comme pointeur vers un type de base de contenir une valeur d’un type dérivé. Polymorphisme (Vient du grec et signifie « Peut prendre plusieurs formes »). Imaginons un jeu d’échec comportant des objets fou, roi, tour, …

luther
Télécharger la présentation

Chapitre VI

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapitre VI Permet à une variable déclarée comme pointeur vers un type de base de contenir une valeur d’un type dérivé. Polymorphisme (Vient du grec et signifie « Peut prendre plusieurs formes ») Imaginons un jeu d’échec comportant des objets fou, roi, tour, … La méthode « mouvement() » pourra, grâce au polymorphisme, effectuer le mouvement approprié d’une pièce grâce au type de pièce qui lui sera associé. Exemple :

  2. Pointeurs d’une classe de base vs pointeurs de classes dérivées Héritage et pointeur de base On peut définir un pointeur vers une classe de base pour stocker l’adresse d’objets dérivés. Si les classes B et et C dérivent de A, on peut définir un pointeur vers la classe A pour stocker soit l’adresse d’un objet de type B soit l’adresse d’un objet de type C. Polygone_2D * P; P = new Polygone_2D; delete P; P = new Polygone_2D_convexe; delete P; Ex.: Cette affectation est possible uniquement parce que la classe Polygone_2D_convexe dérive de la classe Polygone_2D. Chapitre VI - Polymorphisme

  3. Pointeurs d’une classe de base vs pointeurs de classes dérivées Le sens d’affectation est primordial. C++ ne permet pas de stocker l’adresse d’un objet de type Polygone_2D dans un pointeur Polygone_2D_convexe. Attention Il est possible de déclarer une seule variable de type pointeur puis choisir au moment de l’exécution du programme le type d’objet à créer. Intérêt Pour gérer des ensembles d’objets, il suffit de définir un tableau de pointeurs vers la classe de base pour stocker l’adresse de n’importe quel objet dérivé. Intérêt On peut définir un tableau de pointeurs vers des polygones, des polygones convexes et des triangles. Polygone_2D * Tableau[20]; Ex.: Collection hétérogène On peut effectuer un forçage de type explicite pour convertir un pointeur de classe de base en un pointeur de classe dérivée (voir exemples). Chapitre VI - Polymorphisme

  4. Substitution des fonctions membres d’une classe de base dans une classe dérivée La substitution est faite en fournissant une nouvelle version de cette fonction avec la même signature. Une signature différente => une surcharge de fonction et non une substitution de fonction. Note : En mentionnant cette fonction par son nom dans la classe dérivée, la version de la classe dérivée est choisie automatiquement. On peut utiliser l’opérateur de résolution de portée (::) pour accéder à la version de la classe de base à partir de la classe dérivée. Par ex., il arrive souvent que la version de la classe dérivée appelle la version de la classe de base en plus d’effectuer certains travaux additionnels. Chapitre VI - Polymorphisme

  5. Forçage de types explicites et substitution des fonctions membres #include <iostream.h> typedef enum {insecte = 0, vent, animal, eau, humain} mode_de_pollinisation; typedef enum {guepe, coccinelle, mouche, libellule, cigale, abeille, fourmi, sauterelle, patineur} type_insecte; class Pollinisation { protected : mode_de_pollinisation Mode; public : Pollinisation(mode_de_pollinisation M); mode_de_pollinisation Acces_mode(); void Clone(Pollinisation P); }; Chapitre VI - Polymorphisme

  6. Forçage de types explicites et substitution des fonctions membres Pollinisation::Pollinisation(mode_de_pollinisation M) { Mode = M; cout << "Constructeur de la classe de base: " << Mode << endl; } mode_de_pollinisation Pollinisation::Acces_mode() { return Mode; } void Pollinisation::Clone(Pollinisation P) { Mode = P.Acces_mode(); cout << "Mode : " << Mode << endl; } Chapitre VI - Polymorphisme

  7. Forçage de types explicites et substitution des fonctions membres class Pollinisation_par_insecte : public Pollinisation { private : type_insecte Nom_d_insecte; public : Pollinisation_par_insecte(type_insecte Nom); type_insecte Acces_nom(); void Clone(Pollinisation_par_insecte P); }; Pollinisation_par_insecte::Pollinisation_par_insecte(type_insecte Nom) :Pollinisation(insecte) { Nom_d_insecte = Nom; cout << "Constructeur de la classe derivee: " << Nom_d_insecte << endl; } type_insecte Pollinisation_par_insecte::Acces_nom() { return Nom_d_insecte; } Chapitre VI - Polymorphisme

  8. Forçage de types explicites et substitution des fonctions membres void Pollinisation_par_insecte::Clone(Pollinisation_par_insecte P) { Nom_d_insecte = P.Acces_nom(); cout << "Nom : " << Nom_d_insecte << endl; Pollinisation::Clone(P); } void main() { Pollinisation P(vent); Pollinisation Q(eau); Q.Clone(P); Pollinisation_par_insecte R(abeille); Pollinisation_par_insecte S(cigale); R.Clone(S); Chapitre VI - Polymorphisme

  9. Forçage de types explicites et substitution des fonctions membres P.Clone(S); Pollinisation_par_insecte * T = new Pollinisation_par_insecte(fourmi); ((Pollinisation *) T) -> Clone(Q); ((Pollinisation *) T) -> Clone(S); // T -> Clone(Q); Conversion de Q impossible. T -> Clone(S); Pollinisation * U = new Pollinisation(insecte); // ((Pollinisation_par_insecte *) U) -> Clone(Q); Conversion de Q impossible. ((Pollinisation_par_insecte *) U) -> Clone(S); U -> Clone(Q); U -> Clone(S); } Chapitre VI - Polymorphisme

  10. Forçage de types explicites et substitution des fonctions membres Constructeur de la classe de base: 1 Mode : 0 Constructeur de la classe de base: 3 Constructeur de la classe de base: 0 Mode : 1 Nom : 4 Constructeur de la classe de base: 0 Mode : 0 Constructeur de la classe dérivée: 5 Mode : 1 Constructeur de la classe de base: 0 Mode : 0 Constructeur de la classe dérivée: 4 Nom : 4 Mode : 0 Mode : 0 Constructeur de la classe de base: 0 Constructeur de la classe dérivée: 6 Mode : 1 Mode : 0 Nom : 4 Chapitre VI - Polymorphisme

  11. Exemple : classe Pile (Pile.h) class element { }; class pile { /* Spécification fonctionnelle de la classe " pile ". Éléments : Chaque sommet de la pile renferme l'adresse d'un élément, non pas l'élément lui-même où le type de chaque élément est une classe dérivée de la classe de base "element". Structure : Les éléments sont reliés entre eux permettant de déterminer l'ordre d'arrivée des éléments dans la pile. */ Chapitre VI - Polymorphisme

  12. Exemple : classe Pile (Pile.h) protected: struct sommet_pile { element * pElement; struct sommet_pile *suivant; }; struct sommet_pile * pPile; Chapitre VI - Polymorphisme

  13. Exemple : classe Pile (Pile.h) public: void Creer_pile(); /* Permet de créer une pile vide. Pré - Nil. Post - La pile existe et est vide. */ void Inserer(element * pElement); /* Insérer l'adresse d'un élément dans la pile. Pré - La pile a déjà été créée et n'est pas pleine. Post - La pile renferme pElement et l'interprète comme étant l'adresse de l'élément le plus récent inséré dans la pile */ Chapitre VI - Polymorphisme

  14. Exemple : classe Pile (Pile.h) element * Enlever(); /* Enlever un élément de la pile. Pré - La pile a déjà été créée et n'est pas vide. Post - L'adresse de l'élément le plus récent inséré dans la pile est retourné; cet élément ne fait plus partie de la pile. */ bool Pile_vide(); /* Vérifier si la pile est vide ou non. Pré - La pile a déjà été créée. Post - Si la pile ne possède aucun élément alors retourner true sinon retourner false. */ Chapitre VI - Polymorphisme

  15. Exemple : classe Pile (Pile.h) bool Pile_pleine(); /* Vérifier si la pile est pleine ou non. Pré - La pile a déjà été créée. Post - Si la pile a atteint sa capacité maximale alors retourner vrai sinon retourner faux. */ void Vider_pile(); /* Vider la pile. Pré - La pile a déjà été créée. Post - La pile est vide. */ }; Chapitre VI - Polymorphisme

  16. Exemple: classe Pile (Pile.cpp) #include <iostream.h> #include "Pile.h" void pile::Creer_pile() { pPile = NULL; } void pile::Inserer(element * pElement) { struct sommet_pile *pe = new sommet_pile; (*pe).pElement = pElement; (*pe).suivant = pPile; pPile = pe; } Chapitre VI - Polymorphisme

  17. Exemple : classe Pile (Pile.cpp) element * pile::Enlever() { element * pElement; struct sommet_pile *pe = NULL; pElement = (*pPile).pElement; pe = pPile; pPile = (*pPile).suivant; delete(pe); return pElement; } Chapitre VI - Polymorphisme

  18. Exemple : classe Pile (Pile.cpp) bool pile::Pile_vide() { if (pPile == NULL ) return true; else return false; } bool pile::Pile_pleine() { /* Il n'y a aucune façon de tester si la liste chaînée est pleine i.e. s'il existe encore de l'espace disponible pour un autre sommet "sommet_pile". */ return false; } Chapitre VI - Polymorphisme

  19. Exemple : classe Pile (Pile.cpp) void pile::Vider_pile() { element * pElement; while (Pile_vide() == false) pElement = Enlever(); } Chapitre VI - Polymorphisme

  20. Utilisation de plusieurs piles class caractere: public element { public: char c; }; #include <iostream.h> #include "pile.h" class entier: public element { public: int e; }; class reel: public element { public: float r; }; Chapitre VI - Polymorphisme

  21. Utilisation de plusieurs piles void main() { entier i, j, k; reel a, b; caractere u; i.e = 0; j.e = 1; k.e = 2; a.r = 0.0f; b.r = 0.1f; u.c = 'a'; pile Pile_entiers, Pile_reels, Pile_caracteres; Pile_entiers.Creer_pile(); Pile_reels.Creer_pile(); Pile_caracteres.Creer_pile(); Chapitre VI - Polymorphisme

  22. Utilisation de plusieurs piles Pile_entiers.Inserer(&i); Pile_entiers.Inserer(&j); Pile_entiers.Inserer(&k); Pile_reels.Inserer(&a); Pile_reels.Inserer(&b); Pile_caracteres.Inserer(&u); cout << (* (entier *) Pile_entiers.Enlever()).e; cout << (* (entier *) Pile_entiers.Enlever()).e; cout << (* (entier *) Pile_entiers.Enlever()).e; cout << (* (reel *) Pile_reels.Enlever()).r; cout << (* (reel *) Pile_reels.Enlever()).r; cout << (* (caractere *) Pile_caracteres.Enlever()).c; Chapitre VI - Polymorphisme

  23. Utilisation de plusieurs piles if ((Pile_entiers.Pile_vide() == true) && (Pile_reels.Pile_vide() == true) && (Pile_caracteres.Pile_vide() == true)) cout << "Les 3 piles sont vides."; } Chapitre VI - Polymorphisme

  24. Une pile de dossiers #include <iostream.h> #include <string.h> #include "pile.h" class dossier: public element { protected: int code; char description[20+1]; float priorite; public: dossier(int c, char * d ="", float p = 0.0f); int Acces_Code(); char * Acces_Description(); float Acces_Priorite(); }; Chapitre VI - Polymorphisme

  25. Une pile de dossiers dossier::dossier(int c, char * d, float p) { code = c; priorite = p; strcpy(description, d); } int dossier::Acces_Code() { return code; } Chapitre VI - Polymorphisme

  26. Une pile de dossiers char * dossier::Acces_Description() { return description; } float dossier::Acces_Priorite() { return priorite; } Chapitre VI - Polymorphisme

  27. Une pile de dossiers void main() { dossier c(36); dossier b(12, "description I : ", 0.25); dossier a(24, "description II : "); pile Pile_dossiers; Pile_dossiers.Creer_pile(); Pile_dossiers.Inserer(&a); Pile_dossiers.Inserer(&b); Pile_dossiers.Inserer(&c); Chapitre VI - Polymorphisme

  28. Une pile de dossiers cout << (* (dossier *) Pile_dossiers.Enlever()).Acces_Code(); cout << (* (dossier *) Pile_dossiers.Enlever()).Acces_Description(); cout << (* (dossier *) Pile_dossiers.Enlever()).Acces_Priorite(); if (Pile_dossiers.Pile_vide() == true) cout << "La pile est vide."; } FIN Chapitre VI - Polymorphisme

  29. Une forêt, pas un arbre Certains langages de programmation orientés objet (comme par ex. Smalltalk et Java) placent tous les objets dans une unique grande hiérarchie d’héritage : Tous les objets dans ces langages descendent d’une unique classe de base nommée le plus souvent Object. Avantages : Un pointeur vers un objet de cette classe peut renfermer tout type de valeur. Tout comportement défini dans la classe Object est commun à toutes les classes. Le langage C++ ne procède pas ainsi; il permet aux programmeurs de créer une forêt de nombreux petits arbres d’héritage. Chapitre VI - Polymorphisme

  30. Polymorphisme Pourquoi les notions d’héritage et de pointeurs de base ne peuvent s’appliquer à la construction de la classe Vecteur pour éliminer la surcharge de fonctions? Technique associée au polymorphisme: la redéfinition des fonctions membres des classes. INTÉRÊT Le polymorphisme permet à des objets de classes différentes liées par héritage de répondre de façon distincte à un appel de fonction membre. Chapitre VI - Polymorphisme

  31. Exemple permettant d’introduire le polymorphisme Tiré de [Dupin, 99; pp. 247-253] #include <iostream.h> #include <string.h> class materiel { protected: char Reference[20+1]; char Marque[20+1]; public: Materiel(char *r, char *m); void Affiche(); }; class Micro : public Materiel { protected: char Processeur[20+1]; int Disque; public: Micro(char *r, char *m, char *p, int d); void Affiche(); }; Chapitre VI - Polymorphisme

  32. Exemple permettant d’introduire le polymorphisme Micro :: Micro(char *r, char *m, char *p, int d) :Materiel(r,m) { strcpy(Processeur, p); Disque = d; } void Micro::Affiche() { cout << "Ref : "   << Reference; cout << "Marque : "   << Marque; cout << "Proc : "   << Processeur; cout << "Disque : "   << Disque; cout <<   "\n"; } Materiel::Materiel(char *r, char *m) { strcpy(Reference, r); strcpy(Marque, m); } void Materiel::Affiche() { cout << "Ref : "   << Reference; cout << "Marque : "   << Marque; cout <<   "\n"; } Chapitre VI - Polymorphisme

  33. Exemple permettant d’introduire le polymorphisme int main() { Materiel *pMat; pMat = new Materiel("X01", "XX"); pMat -> Affiche(); delete pMat; pMat = new Micro("X16",  "PH",   "DX4-100", 200); pMat -> Affiche(); delete pMat; return 0; } Ref : X01 Marque : XX Ref : X16 Marque : PH Chapitre VI - Polymorphisme

  34. Exemple permettant d’introduire le polymorphisme Erreur Les 2 classes possèdent une fonction Affiche(). Le résultat affiché est invalide car c’est la méthode Affiche() de Materiel qui a été utilisée chaque fois. Pourquoi? Le compilateur a tenu compte du type de pointeur (Materiel *) et non pas du type d’objet pointé par pMat. Comportement non polymorphique Il arrive qu’une fonction membre non virtual soit définie dans une classe de base et redéfinie dans une classe dérivée. Si on appelle cette fonction par un pointeur de classe de base vers l’objet de classe dérivée, la version de classe de base est alors utilisée. Si on appelle cette fonction au moyen d’un pointeur de classe dérivée, la version utilisée est alors celle de classe dérivée.

  35. Exemple permettant d’introduire le polymorphisme Solution Il faudrait que le compilateur appelle la fonction Affiche() de Micro (resp. Materiel) quand pMat contient l’adresse d’un objet de type Micro (resp. Materiel). Il faut indiquer au compilateur que la fonction Affiche() est une fonction polymorphe, i.e. en C++, une fonction virtuelle. Il suffit de faire précéder son prototype par le mot clé virtual. Il n’est pas nécessaire d’utiliser ce mot clé devant le corps de la fonction. Dès qu’une fonction est déclarée virtual, elle reste virtuelle à tous les niveaux de la hiérarchie d’héritage, même si elle n’est pas déclarée virtual lorsqu’elle est remplacée par une autre classe. Ceci dit, il est préférable de déclarer explicitement ces fonctions comme virtual à chaque niveau hiérarchique, pour favoriser la clarté du programme.

  36. Déclaration d’une fonction membre polymorphe (virtuelle) Exemple précédent: class Micro : public Materiel { protected: char Processeur[20+1]; int Disque; public: Micro(char *r, char *m, char *p, int d); virtual void Affiche(); }; class materiel { protected: char Reference[20+1]; char Marque[20+1]; public: Materiel(char *r, char *m); virtual void Affiche(); }; Chapitre VI - Polymorphisme

  37. Déclaration d’une fonction membre polymorphe (virtuelle) Ref : X01 Marque : XX Ref : X16 Marque : PH Processeur : DX4-100 Disque : 200  Une fonction polymorphe est une méthode qui est appelée en fonction du type d’objet et non pas en fonction du type de pointeur utilisé. Nous n’avons pas besoin d’utiliser une instruction switch qui exécuterait une action appropriée au type de chaque objet traité. Grâce au polymorphisme, 2 classes peuvent réagir différemment au même appel de méthode. On peut donc constituer des hiérarchies de classes qui partagent une trame commune et qui se différencient les unes des autres.

  38. Déclaration d’une fonction membre polymorphe (virtuelle) Lorsque vous appelez une fonction virtuelle pour un objet, le C++ cherche cette méthode dans la classe correspondante. Si cette fonction n’existe pas dans la classe concernée, le C++ remonte la hiérarchie des classes jusqu’à ce qu’il trouve la fonction appelée. La résolution des appels de fonctions virtuelles a lieu à l’exécution des programmes car ce n’est qu’à cet instant, que le type d’objet pointé sera connu par C++. Toutefois, lorsque nous appelons une fonction virtual en référençant un objet spécifique par son nom, comme dans objet.Affiche(), la référence est résolue lors de la compilation et la fonction virtual appelée est celle définie pour la classe de cet objet particulier ou héritée par elle. Fonctions non virtuelles : le choix de la méthode se fait au moment de la compilation du programme. Chapitre VI - Polymorphisme

  39. Exemple : Ajout d ’une nouvelle fonction à la classe Polygone_2D Ajout de la fonction Point_interieur_Polygone_2D Chapitre VI - Polymorphisme

  40. Exemple : Ajout d’une nouvelle fonction à la classe Polygone_2D Polygone quelconque Polygone Convexe Point intérieur Chapitre VI - Polymorphisme

  41. Exemple : Ajout d’une nouvelle fonction à la classe Polygone_2D Fichier Polygone_2D.h virtual void Point_interieur_Polygone_2D(float *x, float *y); /* Permet de calculer un point intérieur au polygone 2D quelconque. Pré - Le polygone 2D a déjà été créé et le nombre de sommets est >= 3. Post - Retourne en paramètres les coordonnées réelles d'un point intérieur au polygone 2D. */ Chapitre VI - Polymorphisme

  42. Exemple : Ajout d’une nouvelle fonction à la classe Polygone_2D Fichier Polygone_2D_convexe.h virtual void Point_interieur_Polygone_2D(float *x, float *y); /* Permet de calculer un point intérieur au polygone 2D convexe. Pré - Le polygone 2D a déjà été créé et le nombre de sommets est >= 3. Post - Retourne en paramètres les coordonnées réelles d'un point intérieur au polygone 2D. */ Chapitre VI - Polymorphisme

  43. Exemple : Ajout d’une nouvelle fonction à la classe Polygone_2D Fichier Triangle_2D.h Aucun ajout. Fichier Polygone_2D.cpp void Polygone_2D::Point_interieur_Polygone_2D(float *x, float *y) { /***************************************************** */ /* à développer */ /******************************************************/ *x = 0.0; *y = 0.0; } Chapitre VI - Polymorphisme

  44. Exemple : Ajout d’une nouvelle fonction à la classe Polygone_2D Fichier Polygone_2D_convexe.cpp void Polygone_2D_convexe:: Point_interieur_Polygone_2D(float *x, float *y) { float u, v; Point_visible_Polygone_2D_convexe(&u, &v); *x = u; *y = v; } Fichier Triangle_2D.cpp Aucun ajout. Chapitre VI - Polymorphisme

  45. Exemple : Ajout d’une nouvelle fonction à la classe Polygone_2D Fichier Application.cpp #include <iostream.h> #include "Triangle_2D.h" void main() { float a, b; Polygone_2D P; Triangle_2D Q; P.Ajouter_sommet(1, 1); Q.Ajouter_sommet(1, 1); P.Ajouter_sommet(1, 3); Q.Ajouter_sommet(1, 3); P.Ajouter_sommet(4, 3); Q.Ajouter_sommet(4, 3); Chapitre VI - Polymorphisme

  46. Exemple : Ajout d’une nouvelle fonction à la classe Polygone_2D Fichier Application.cpp(suite) if (Q.Acces_statut() == triangle) cout << " Ceci est un triangle."; cout << "Nombre de sommets : " << Q.Nombre_de_sommets(); cout << "Aire du triangle 2D : " << Q.Aire_Polygone_2D(); Q.Point_visible_Polygone_2D_convexe(&a, &b); cout << "Point visible : " << a << " , " << b; Q.Point_interieur_Polygone_2D(&a, &b); cout << "Point interieur de Q: " << a << " , " << b; P.Point_interieur_Polygone_2D(&a, &b); cout << "Point interieur de P: " << a << " , " << b; Chapitre VI - Polymorphisme

  47. Exemple : Ajout d’une nouvelle fonction à la classe Polygone_2D Fichier Application.cpp (suite) P.Detruire_Polygone_2D(); Q.Detruire_Polygone_2D(); cout << "Nombre de sommets de Q: " << Q.Nombre_de_sommets() << "Nombre de sommets de P: " << P.Nombre_de_sommets(); } FIN Chapitre VI - Polymorphisme

  48. Polymorphisme, pour quoi faire? Dès que vous souhaitez modifier le comportement d’une classe de base ou la spécialiser. 3 raisons pour redéfinir une fonction virtuelle dans une classe dérivée: pour la substituer au traitement standard préciser dans cette fonction le nouveau traitement. pour compléter le traitement standard appeler la méthode de la classe de base dans votre fonction ajouter d’autres traitements. pour annuler le traitement standard définir la fonction virtuelle dans votre classe ne rien insérer à l’intérieur du corps de la méthode. Chapitre VI - Polymorphisme

  49. Polymorphisme, pour quoi faire? Rien ne nous oblige à redéfinir une fonction virtuelle dans une classe dérivée si le traitement réalisé par la classe de base vous convient. Généralisation de l’appel des fonctions polymorphes Nous avons appelé jusqu’à maintenant les fonctions polymorphes directement en désignant l’objet traité. Qu’arrive-t-il lorsque cette fonction polymorphe est utilisée par une autre méthode de la classe ? Chapitre VI - Polymorphisme

  50. Généralisation de l’appel des fonctions polymorphes Tiré de [Dupin, 99; pp. 258-260] #include <iostream.h> #include <string.h> class materiel { protected: char Reference[20+1]; char Marque[20+1]; public: Materiel(char *r, char *m); virtual void Affiche(); void AfficheTitre(); }; class Micro : public Materiel { protected: char Processeur[20+1]; int Disque; public: Micro(char *r, char *m, char *p, int d); virtual void Affiche(); }; Ajout d ’une nouvelle méthode Chapitre VI - Polymorphisme

More Related