La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

(Vient du grec et signifie « Peut prendre plusieurs formes »)

Présentations similaires


Présentation au sujet: "(Vient du grec et signifie « Peut prendre plusieurs formes »)"— Transcription de la présentation:

1 (Vient du grec et signifie « Peut prendre plusieurs formes »)
Chapitre VI Polymorphisme (Vient du grec et signifie « Peut prendre plusieurs formes ») Permet à une variable déclarée comme pointeur vers un type de base de contenir une valeur d’un type dérivé. Exemple : 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é.

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. Ex.: Polygone_2D * P; P = new Polygone_2D; delete P; P = new Polygone_2D_convexe; 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 Chapitre VI - Polymorphisme
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 dérivée: 4 Nom : 4 Mode : 0 Constructeur de la classe de base: 0 Constructeur de la classe dérivée: 6 Mode : 1 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. 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
#include <iostream.h> #include "pile.h" class entier: public element { public: int e; }; class reel: public element float r; class caractere: public element { public: char c; }; 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 << (* (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 Chapitre VI - Polymorphisme
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 Chapitre VI - Polymorphisme
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 Chapitre VI - Polymorphisme
Une pile de dossiers char * dossier::Acces_Description() { return description; } float dossier::Acces_Priorite() return priorite; Chapitre VI - Polymorphisme

27 Chapitre VI - Polymorphisme
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 Chapitre VI - Polymorphisme
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 Chapitre VI - Polymorphisme
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 Chapitre VI - 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 ] #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); 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 ] #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

51 Généralisation de l’appel des fonctions polymorphes
Materiel::Materiel(char *r, char *m) { strcpy(Reference, r); strcpy(Marque, m); } void Materiel::Affiche() cout << "Ref : "   << Reference; cout << "Marque : "   << Marque; cout <<   "\n"; void Materiel::AfficheTitre() { cout <<   "DEBUT\n "; Affiche(); cout <<  "FIN\n"; } Micro :: Micro(char *r, char *m, char *p, int d) :Materiel(r,m) strcpy(Processeur, p); Disque = d; Chapitre VI - Polymorphisme

52 Généralisation de l’appel des fonctions polymorphes
void Micro::Affiche() { cout << "Ref : "   << Reference; cout << "Marque : "   << Marque; cout << "Proc : "   << Processeur; cout << "Disque : "   << Disque; cout <<   "\n"; } DEBUT Ref : X16 Marque : PH Processeur : DX4-100 Disque : 200 FIN void main() { Micro *pMic = NULL; pMic = new Micro("X16",  "PH",   "DX4-100", 200); pMic -> AfficheTitre(); delete pMic; } Chapitre VI - Polymorphisme

53 Généralisation de l’appel des fonctions polymorphes
L’exécution de la fonction AfficheTitre() entraîne l’appel à une fonction Affiche(), laquelle? C++ exécutera la méthode virtuelle en fonction du type d’objet, quel que soit l’endroit d’où provient cet appel i.e. la fonction Affiche() de Micro. Avantage On peut modifier un traitement standard sans y toucher. Les fonctions virtuelles ne peuvent pas être utilisées dans le corps des constructeurs. Un constructeur appellera toujours la fonction définie dans sa classe ou à défaut celle d’une classe de base, mais jamais la fonction virtuelle d’une classe dérivée. Note : Les constructeurs ne peuvent pas être virtuels. Chapitre VI - Polymorphisme

54 Généralisation de l’appel des fonctions polymorphes
Le corps du constructeur de la classe de base s’exécute avant celui de la classe dérivée. Pourquoi? Ainsi, au moment de l’exécution du constructeur de la classe de base, l’initialisation à réaliser par le constructeur de la classe dérivée n’est pas effectuée. Un destructeur ne peut pas appeler une fonction virtuelle d’une classe dérivée. Le destructeur de la classe de base est appelé après celui de la classe dérivée. Pourquoi? Chapitre VI - Polymorphisme

55 Appel de la fonction membre d’une classe de base
Au lieu de substituer au traitement standard, dans certains cas, il est préférable de compléter le traitement de la classe de base plutôt que de le remplacer. Il s’agit d’appeler la fonction virtuelle d’origine à partir de la nouvelle méthode. La fonction Affiche() de Micro utilise la fonction Affiche() de la classe Materiel. Ex. void Micro::Affiche() { Materiel::Affiche(); cout <<   " Processeur : "   << Processeur; cout << "  Disque : "   << Disque; cout << "\n "; } Réutilisation de code Appel de la fonction de la classe de base Chapitre VI - Polymorphisme

56 Appel de la fonction membre d’une classe de base
Si la fonction Affiche() de Materiel est modifiée, les classes dérivées en bénéficieront. Avantage Le nom complet de la fonction Affiche() de la classe Materiel doit être utilisé pour éviter un appel récursif. Chapitre VI - Polymorphisme

57 Polymorphisme et définition d’un destructeur
Si un objet est détruit explicitement par l’application de l’opérateur delete sur un pointeur de classe de base vers l’objet, la fonction de destructeur de classe de base est appelée sur l’objet, indépendamment du type d’objet pointé par le pointeur de la classe de base. Lorsque vous définissez un destructeur de la classe de base, vous devez donc le déclarer en tant que fonction virtuelle. Autrement, la mémoire ne sera pas complètement libérée. Cela rend automatiquement virtuels les destructeurs de toutes les classes dérivées. Dès lors, si un objet de la hiérarchie est détruit explicitement avec delete sur un pointeur de classe de base vers l’objet de classe dérivée, le destructeur de la classe appropriée est appelé. Chapitre VI - Polymorphisme

58 Polymorphisme et définition d’un destructeur
Ex. : Polygone_2D * P; P = new Polygone_2D_convexe; delete P; Si la classe Polygone_2D_convexe contenait un destructeur, et qu’il n’est pas virtuel, C++ utilisera le type de pointeur et non pas le type d’objet et détruira seulement la partie Polygone_2D de cet objet. Rappel Lorsqu’un objet de la classe dérivée est détruit, la partie classe de base de cet objet est également détruite, car le destructeur de classe de base s’exécute automatiquement après le destructeur de classe dérivée. En pratique Si une classe possède des fonctions virtuelles, fournissez-lui un destructeur virtuel même si ce dernier n’est pas obligatoire pour cette classe. Les classes dérivées de cette classe peuvent en effet contenir des destructeurs qui doivent être appelés correctement.

59 Classes abstraites et fonctions virtuelles pures
La plupart du temps, lorsqu’on crée une classe, c’est dans le but de créer des instances de cette classe. Ce n’est pas toujours le cas … Une classe abstraite est une classe qui ne peut être instanciée, i.e. on ne peut pas créer d’objet directement à partir de cette classe, mais on le peut grâce au mécanisme de l’héritage. Même si nous ne pouvons pas instancier des objets de classes de base abstraites, nous pouvons quand même déclarer des pointeurs et des références vers ces classes pour manipuler des objets de classes dérivées de façon polymorphique lorsque ces objets sont instanciés à partir de classes concrètes (non abstraites). On peut se servir d’une telle classe pour regrouper des concepts sous une classe théorique. On peut aussi vouloir imposer une interface générale que toutes les classes héritières devront implanter. Une classe devient abstraite si elle possède au moins une fonction virtuelle pure. Chapitre VI - Polymorphisme

60 Classes abstraites et fonctions virtuelles pures
Une fonction virtuelle pure est une fonction virtuelle pour laquelle le prototype est suivi de l’expression = 0. virtual void Affiche() = 0; Ex.: 2 incidences: Une classe qui contient la définition d’une fonction virtuelle pure devient une classe abstraite. (i) La redéfinition des fonctions virtuelles pures est obligatoire dans toutes les classes dérivées. Autrement, les classes dérivées deviennent également abstraites. (ii) Oblige les classes dérivées à redéfinir des fonctions membres de la classe de base sous peine de devenir elles-mêmes abstraites. Avantage: De cette manière, les classes dérivées n’utiliseront jamais la méthode de la classe de base. Chapitre VI - Polymorphisme

61 Classes abstraites et fonctions virtuelles pures
Figure_geometrique_2D « hérite de » « hérite de » Conique enum type_de_figure{polygone, conique}; class Figure_geometrique_2D //classe abstraite { protected: type_de_figure Figure; public: virtual type_de_figure Acces_type_figure()=0; }; à redéfinir dans les classes dérivées Chapitre VI - Polymorphisme

62 Exemple de classes abstraites
#include "Point_3D.h" enum type_de_courbe_parametrique {constante_3D, segment_3D, courbe_de_Bezier, courbe_parametrique_de_degre_3, courbe_parametrique_quelconque}; /* Cette classe abstraite permet de définir et de manipuler des courbes paramétriques dans [0, 1] : courbe constante, segment 3D, courbe de Bézier et courbe de degré 3. Elle permet aussi de définir des courbes paramétriques quelconques (des courbes qui ne sont pas définies dans PGC++ et pour lesquelles, il n'est pas pertinent de le faire) dans [0, 1] en construisant des classes dérivées de cette classe abstraite. Cette classe renferme des fonctions virtuelles pures lesquelles devront être redéfinies obligatoirement dans les classes dérivées. */

63 Exemple de classes abstraites
class Courbe_parametrique { protected : type_de_courbe_parametrique Courbe; public : Courbe_parametrique(type_de_courbe_parametrique T) { Courbe = T;} /* Permet de créer une courbe paramétrique d'un certain type. Pré - Nil. Post - Une courbe de type T est créée. */ virtual type_de_courbe_parametrique Acces_type_de_courbe_parametrique() = 0; /* Permet d'identifier le type de courbe paramétrique qui a été définie. Pré - La courbe doit avoir été définie. Post - Retourne le type de courbe paramétrique créée.*/

64 Exemple de classes abstraites
virtual Point_3D & operator ()(float u) = 0; /* Permet d'évaluer la courbe courante à u dans l'intervalle [0, 1]. Pré - La courbe courante est créée. Post - Retourne le point sur la courbe à u. */ virtual Vecteur_3D & Tangente(float u) = 0; /* Permet de calculer la tangente unitaire à la courbe à u. Post - Retourne la tangente unitaire à la courbe à u. */ virtual float Longueur(float u1, float u2) = 0; /* Permet de calculer la longueur de la courbe dans l'intervalle [u1, u2]. Pré - La courbe courante est créée. 0 <= u1, u2 <= 1. Post - Retourne la longueur de la courbe dans l'intervalle [u1, u2] */

65 Exemple de classes abstraites
virtual bool Courbe_plane() = 0; /* Permet de déterminer si une courbe 3D est plane ou non. Pré - La courbe courante est créée. Post - Retourne true si la courbe est plane. False autrement. La courbe n'est pas considérée plane lorsqu'il s'agit d'un point ou d'un segment de droite. */ }; Chapitre VI - Polymorphisme

66 Exemple de classes abstraites
#include "Courbe_parametrique.h" class Segment_3D : public Courbe_parametrique { protected : Point_3D S1; Point_3D S2; public : Segment_3D(Point_3D & P1, Point_3D & P2); /* Permet de créer un segment de droite 3D. Pré - Nil. Post - Un segment de droite 3D d'extrémités P1 et P2 est créée. */ type_de_courbe_parametrique Acces_type_de_courbe_parametrique(); /* Permet d'identifier le type de courbe parametrique définie. Pré - La courbe doit avoir été définie. Post - Retourne le type de courbe parametrique créée, soit un segment de droite. */

67 Exemple de classes abstraites
void Acces_definition_segment_3D(Point_3D * P1, Point_3D * P2); /* Donne accès à la définition du segment 3D courant. Pré - La courbe doit avoir été définie. Post - Retourne les extrémités du segment 3D courant. */ Segment_3D & operator = (Segment_3D & S); /* Permet d'affecter S à la courbe courante. Pré - S est créée. Le segment courant est créé. Post - La courbe courante renferme maintenant la définition de S.*/ Point_3D & operator[](int i); /* Permet d'accéder ou de modifier l'extrémité i (= 1 ou 2) du segment de droite courant. Pré - Le segment de droite courant est créé et initialisé. Post - Accède ou modifie l'extrémité i (= 1 ou 2) du segment de droite courant. */

68 Exemple de classes abstraites
Point_3D & operator ()(float u); /* Permet d'évaluer la courbe courante à u dans l'intervalle [0, 1]. Pré - La courbe courante est créée. Post - Retourne le point sur la courbe à u. */ Vecteur_3D & Tangente(float u); /* Permet de calculer la tangente au segment de droite à u. Post - Retourne le vecteur S2 - S1. */ Chapitre VI - Polymorphisme

69 Exemple de classes abstraites
float Longueur(float u1, float u2); /* Permet de calculer la longueur du segment de droite dans l'intervalle [u1, u2]. Pré - Le segment de droite est créé. 0 <= u1, u2 <= 1. Post - Retourne la longueur du segment de droite dans l'intervalle [u1, u2]. */ bool Courbe_plane(); /* Pré - La courbe courante est créée. Post - Retourne false car la courbe n'est pas considérée plane lorsqu’il s'agit d'un point ou d'un segment de droite. */ }; Segment_3D.cpp Chapitre VI - Polymorphisme

70 Système de paie utilisant le polymorphisme
// Exemple tiré du livre "Comment programmer en C++, Deitel et Deitel, // Eyrolles, pp #include <iostream.h> #include <string.h> #include <iomanip.h> class Employe // Classe de base abstraite { private: char * prenom; char * nomFamille; public: Employe(const char * prenom, const char * nom); ~Employe(); const char * lecturePrenom() const; const char * lectureNomFamille() const; virtual double gains() const = 0; // Fonction virtuelle pure virtual void affichage() const; // Fonction virtuelle };

71 Système de paie utilisant le polymorphisme
Employe::Employe(const char * premier, const char * dernier) { prenom = new char[strlen(premier) + 1]; strcpy(prenom, premier); nomFamille = new char[strlen(dernier) + 1]; strcpy(nomFamille, dernier); } Employe::~Employe() delete [] prenom; delete [] nomFamille; Chapitre VI - Polymorphisme

72 Système de paie utilisant le polymorphisme
// Renvoie un pointeur vers le prénom. Un renvoi de type const empêche // l'appelant de modifier les données private. L'appelant doit copier // la chaîne renvoyée avant que le destructeur ne supprime la mémoire // dynamique pour empêcher la formation d'un pointeur non défini. const char * Employe::lecturePrenom() const { return prenom; } // Renvoie un pointeur vers le nom de famille. Un renvoi de type const // empêche l'appelant de modifier les données private. L'appelant doit // copier la chaîne renvoyée avant que le destructeur ne supprime la // mémoire dynamique pour empêcher la formation d'un pointeur non défini. const char * Employe::lectureNomFamille() const { return nomFamille; Chapitre VI - Polymorphisme

73 Système de paie utilisant le polymorphisme
void Employe::affichage() const { cout << prenom << " " << nomFamille; } // Fin de la classe Employe Chapitre VI - Polymorphisme

74 Système de paie utilisant le polymorphisme
class Patron: public Employe // Classe dérivée de la classe Employe { private: double salaireHebdo; public: Patron(const char * prenom, const char * nom, double salaireHebdo = 0.0); void ajusterSalaireHebdo(double salaireHebdo); // Ajuste le salaire du patron. virtual double gains() const; // Lit la paie du patron. virtual void affichage() const; // Affiche le nom du patron. }; Chapitre VI - Polymorphisme

75 Système de paie utilisant le polymorphisme
Patron::Patron(const char * premier, const char * dernier, double salaire) : Employe(premier, dernier) { ajusterSalaireHebdo(salaire); } void Patron::ajusterSalaireHebdo(double salaire) { salaireHebdo = salaire; double Patron::gains() const { return salaireHebdo; void Patron::affichage() const { cout << "\n Patron: "; Employe::affichage(); // Fin de la classe Patron

76 Système de paie utilisant le polymorphisme
class EmployeCommission: public Employe // Classe dérivée de la classe Employe { private: double salaire; // salaire hebdomadaire de base double commission; // montant par article vendu int quantite; // total d'articles vendus pour la semaine public: EmployeCommission(const char * prenom, const char * nom, double salaire = 0.0, double commission = 0.0, int quantite = 0); void ajusterSalaire(double salaire); // Ajuste le salaire. void ajusterCommission(double commission); // Ajuste la commission. void ajusterQuantite(int quantite); // Ajuste le total d'articles vendus. virtual double gains() const; // Lit la paie de l'employé à commission. virtual void affichage() const; // Affiche le nom de l'employé à commission. };

77 Système de paie utilisant le polymorphisme
EmployeCommission::EmployeCommission(const char * premier, const char * dernier, double s, double c, int q) : Employe(premier, dernier) { ajusterSalaire(s); ajusterCommission(c); ajusterQuantite(q); } void EmployeCommission::ajusterSalaire(double s) salaire = s; void EmployeCommission::ajusterCommission(double c) commission = c; Chapitre VI - Polymorphisme

78 Système de paie utilisant le polymorphisme
void EmployeCommission::ajusterQuantite(int q) { quantite = q; } double EmployeCommission::gains() const return salaire + commission * quantite; void EmployeCommission::affichage() const cout << "\n Employe a commission: "; Employe::affichage(); // Fin de la classe EmployeCommission Chapitre VI - Polymorphisme

79 Système de paie utilisant le polymorphisme
class EmployePiece: public Employe // Classe dérivée de la classe Employe { private: double tauxParPiece; // taux pour chaque pièce produite int quantite; // quantité produite pour la semaine public: EmployePiece(const char * prenom, const char * nom, double tauxParPiece = 0.0, int quantite = 0); void ajusterPaie(double tauxParPiece); // Ajuste le taux par pièce. void ajusterQuantite(int quantite); // Ajuste la qté produite pour la semaine. virtual double gains() const; // Lit la paie de l'employé à la pièce. virtual void affichage() const; // Affiche le nom de l'employé à la pièce. }; Chapitre VI - Polymorphisme

80 Système de paie utilisant le polymorphisme
EmployePiece::EmployePiece(const char * premier, const char * dernier, double w, int q) : Employe(premier, dernier) { ajusterPaie(w); ajusterQuantite(q); } void EmployePiece::ajusterPaie(double w) tauxParPiece = w; void EmployePiece::ajusterQuantite(int q) quantite = q; Chapitre VI - Polymorphisme

81 Système de paie utilisant le polymorphisme
double EmployePiece::gains() const { return tauxParPiece * quantite; } void EmployePiece::affichage() const cout << "\nEmploye paye a la piece: "; Employe::affichage(); // Fin de la classe EmployePiece Chapitre VI - Polymorphisme

82 Système de paie utilisant le polymorphisme
class EmployeHoraire: public Employe // Classe dérivée de la classe Employe { private: double tauxHoraire; // taux horaire double heures; // heures travaillées pour la semaine public: EmployeHoraire(const char * prenom, const char * nom, double tauxHoraire = 0.0, double heures = 0); void ajusterPaie(double tauxHoraire); // Ajuste le taux horaire. void ajusterHeures(double heures); // Ajuste les heures travaillées pour la semaine. virtual double gains() const; // Lit la paie de l'employé à l'heure. virtual void affichage() const; // Affiche le nom de l'employé à l'heure };

83 Système de paie utilisant le polymorphisme
EmployeHoraire::EmployeHoraire(const char * premier, const char * dernier, double w, double h) : Employe(premier, dernier) { ajusterPaie(w); ajusterHeures(h); } void EmployeHoraire::ajusterPaie(double w) tauxHoraire = w; void EmployeHoraire::ajusterHeures(double h) heures = h; Chapitre VI - Polymorphisme

84 Système de paie utilisant le polymorphisme
double EmployeHoraire::gains() const { if (heures <= 40) return tauxHoraire * heures; else return 40 * tauxHoraire + (heures - 40) * tauxHoraire * 1.5; } void EmployeHoraire::affichage() const cout << "\n Employe horaire: "; Employe::affichage(); // Fin de la classe EmployeHoraire Chapitre VI - Polymorphisme

85 Système de paie utilisant le polymorphisme
void virtuelViaPointeur( const Employe * E); void virtuelViaReference(const Employe & E); void main() { Patron b("Jean", "Soucy", ); b.affichage(); cout << " a gagne $" << b.gains(); virtuelViaPointeur( &b ); virtuelViaReference( b ); EmployeCommission c("Lise", "Jobin", , 3.0, 150); c.affichage(); cout << " a gagne $" << c.gains(); virtuelViaPointeur( &c ); virtuelViaReference( c );

86 Système de paie utilisant le polymorphisme
EmployePiece p("Benoit", "Cyr", 2.5, 200); p.affichage(); cout << " a gagne $" << p.gains(); virtuelViaPointeur( &p ); virtuelViaReference( p ); EmployeHoraire h("Karine", "Roy", 13.75, 40); h.affichage(); cout << " a gagne $" << h.gains(); virtuelViaPointeur( &h ); virtuelViaReference( h ); cout << endl; }

87 Système de paie utilisant le polymorphisme
void virtuelViaPointeur( const Employe * classeBasePtr ) { classeBasePtr -> affichage(); cout << " a gagne $" << classeBasePtr -> gains(); } void virtuelViaReference( const Employe & classeBaseRef ) classeBaseRef.affichage(); cout << " a gagne $" << classeBaseRef.gains();

88 Système de paie utilisant le polymorphisme
Patron: Jean Soucy a gagne $800.00 Employe a commission: Lise Jobin a gagne $650.00 Employe paye a la piece: Benoit Cyr a gagne $500.00 Employé horaire: Karine Roy a gagné $550.00 FIN FIN FIN FIN FIN FIN FIN FIN FIN FIN FIN FIN FIN FIN FIN


Télécharger ppt "(Vient du grec et signifie « Peut prendre plusieurs formes »)"

Présentations similaires


Annonces Google