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

Structures de données IFT-2000 Abder Alikacem L’héritage en C++ Département d’informatique et de génie logiciel Édition Septembre 2009.

Présentations similaires


Présentation au sujet: "Structures de données IFT-2000 Abder Alikacem L’héritage en C++ Département d’informatique et de génie logiciel Édition Septembre 2009."— Transcription de la présentation:

1 Structures de données IFT-2000 Abder Alikacem L’héritage en C++ Département d’informatique et de génie logiciel Édition Septembre 2009

2 Plan  L’héritage  public  protected  private  Construction des objets  Destruction des objets  Liaison dynamique  Classe abstraite  Héritage multiple  Résumé et synthèse

3 Programmation orienté objet 1 classe = 1 module Parties privée et publique des classes Interface de la classe = type abstrait + généricité (templates) Héritage Modularité Encapsulation Abstraction Extensibilité

4 Pourquoi Hériter ? Relier des objets entre eux (famille) Factoriser des traitements (Réutilisabilité) Représenter le centre de la figure (généralisation) IDENTIFICATION DES OBJETS Un cercle (rayon) ne « ressemble » pas à un rectangle (diagonale) Faciliter l ’extension l’application (Extensibilité) On traite les ellipses

5 Comment Hériter ? GESTION D’UNE HIERARCHIE DE CLASSES  Factorisation Toute figure géométrique a un centre  Spécialisation (un objet est un cas particulier d ’un autre objet) Un carré est un cas particulier de rectangle  Enrichissement (un objet a des propriétés propres) Un triangle rectangle comprend le calcul de l’hypoténuse

6 Relation d’héritage La classe D hérite de la classe M M Classe mère D Classe dérivée Héritage simple : Un unique ascendant direct

7 L’héritage class X: Y { … } public protected private Mode Syntaxe Mode Ancètre Dérivée Private Private Inaccessible Protected Private Public Private Protected Private Inaccessible Protected Protected PublicProtected Public Private Inaccessible ProtectedProtected Public Public

8  Héritage « public » L’héritage  Les classes dérivées héritent de la classe mère en se spécialisant  Les membres public de la classe mère deviennent des membres public des classes dérivées.  Les classes dérivées peuvent avoir des membres supplémentaires. (enrichissement) Héritage public Relation : « est une sorte de »

9 L’héritage « public » class M {public : void f(void); void g(void); private:... }; class D: {public : Méthodes redéfinies : void f(void); Méthodes supplémentaires : void h(void); private: Caractéristiques supplémentaires }; public M Une instance W de la classe D a accès à la section public des classes D et M

10 L’héritage « public » class M {public :... protected : private: }; class D: {public :... private: Caractéristiques supplémentaires }; public M Pour la classe D : les membres de la section protected de la classe mère M sont accessibles par ses fonctions membres et fonctions amies Pour la classe M : les membres de la section protected se comportent comme s’ils étaient placés dans sa section private.

11 L’héritage « public » class M {public : protected: private : }; class D : {public : protected: private : }; public M Partie inaccessible héritée

12 L’héritage « public » class M {public :... protected : private: }; class D: {public :... private: Caractéristiques supplémentaires }; public M (ex: fonction main) accès aux sections public des classes M et D Vision utilisateur accès aux sections public et protected de la classe M Classe D

13 L’héritage « public » class M {public :... protected : private: }; class D: {public :... private: Caractéristiques supplémentaires }; public M Attention : l ’utilisation du qualificateur protected est contraire au principe d ’encapsulation. Si des modifications sont apportées à la section protected de la classe M alors toutes les classes dérivées qui utilisent l ’accès direct à cette section sont susceptibles de subir des modifications.

14 class X{ public: int x1; protected: int x2; private: int x3; }; X x; cout << x.x1 << endl; cout << x.x2 << endl; //NON cout << x.x3 << endl; //NON class Y : public X { public: Y() { x1 = 0;//public x2 = 0;//protected x3 = 0;//NON } }; Y y; cout << y.x1 << endl; cout << y.x2 << endl; //NON cout << y.x3 << endl; //NON  Résumé L’héritage « public »

15 class Y : protected X { public: Y() { x1 = 0;//protected x2 = 0;//protected x3 = 0;//NON } }; Y y; cout << y.x1 << endl; //NON cout << y.x2 << endl; //NON cout << y.x3 << endl; //NON  Cas de l’héritage « protected » class X{ public: int x1; protected: int x2; private: int x3; }; X x; cout << x.x1 << endl; cout << x.x2 << endl; //NON cout << x.x3 << endl; //NON L’héritage

16 L’héritage « private » La classe D hérite de la classe M M Classe mère D Classe dérivée Héritage privé : La classe D restreint les fonctionnalités de M Les membres public de la classe mère et protégés de la classe mère deviennent des membres privés des classes dérivées. Les classes dérivées peuvent avoir des membres supplémentaires. Héritage privé

17 L’héritage « private » class M {public : protected: private : }; class D : {public : protected: private : }; private M Partie inaccessible héritée

18  Exemple L’héritage « private> class Tableau {public: Tableau (void); // Constructeur Tableau (const tableau&); //const. par //copie Tableau& operator=(const tableau&); int& operator [ ](int); ~Tableau (void); // Destructeur private : int * T; int nb; // nombre d ’ elements }; class Pile:private Tableau {public: Pile (int n); // Constructeur void empiler (int) ; int depiler (void); int ValSommet (void); bool vide (void); ~Pile (void); // destructeur private : int sommet; };

19  Suite… Utilisation d’une pile L’héritage « private> int main() { Pile p(10); p.empiler(3); p [2] = 9; return 0; } Vision utilisateur class Pile: private Tableau {public: Pile (int n); // Constructeur void empiler (int) ; int depiler (void); int valSommet (void); bool estVide (void);... }; illégal

20  Suite… Implémentation L’héritage « private> Pile::Pile (int n) {sommet = -1;} void Pile::empiler (int e) {sommet += 1; (*this) [sommet]=e;} void Pile::depiler (void) {sommet -= 1;} int Pile::valSommet (void) {return (*this) [sommet];} bool Pile::estVide (void) {return sommet= = -1;}

21  Résumé L’héritage « private » class Y : private X { public: Y() { x1 = 0;//private x2 = 0;//private x3 = 0;//NON } }; Y y; cout << y.x1 << endl; //NON cout << y.x2 << endl; //NON cout << y.x3 << endl; //NON class X{ public: int x1; protected: int x2; private: int x3; }; X x; cout << x.x1 << endl; cout << x.x2 << endl; //NON cout << x.x3 << endl; //NON

22 L’héritage et le contrôle d’accès  Une classe dérivée dans sa déclaration spécifie de quelle(s) classe(s) elle hérite mais précise aussi le contrôle d'accès des membres hérités : dans tous les cas, ne peut être accessible dans la classe dérivée que la partie publique ou protégée de la classe de base si l'héritage est public, les membres publics et protégés de la classe de base sont respectivement publics et protégés dans la classe dérivée si l'héritage est privé, les membres publics et protégés de la classe de base sont privés dans la classe dérivée si l'héritage est protégé, les membres publics et protégés sont protégés dans la classe dérivée.  on peut déclarer une classe dérivée comme friend dans la classe de base, permettant ainsi l'accès à tous les membres de la classe de base.  par défaut, l'héritage est privé pour une class et public pour une struct

23  Les constructeurs L’héritage Lors de la création d ’une instance d ’une classe dérivée son constructeur et tous les constructeurs de ses classes parentes sont activés.  Comme un objet d'une classe dérivée est un objet de la classe de base (classe mère) plus quelque chose, pour construire un objet de la classe dérivée il faut au préalable construire la partie de l'objet correspondant à la classe de base.

24  Construction des objets class X{ public: int x1; void a() {…} protected: int x2; private: int x3; }; class Y : public X { public:int x1; //redéfini int y; //nouveau void a() {…} // redéfini void b() {…} // nouveau int a() {…} // nouveau }; L’héritage

25  Construction des objets class X{ public: void a() {…} }; class Y : public X { public: void a() {…} // redéfini void b() { X::a(); // autre classe a(); //local } }; Si on veut utiliser une fonction de la classe ancêtre alors qu’elle a été redéfinie dans la classe dérivée, il faut utiliser l’opérateur de portée « :: » L’héritage

26  Construction des objets class X{ public: X() {cout << ‘x’;} }; class Y : public X { public: Y() { cout << ‘y’;} }; Lors de la construction d’un objet des constructeurs de toute la hiérarchie des classes sont appelées dans l’ordre de la hiérarchie « top-down »: les constructeurs sont activés de la racine de l’arbre d’héritage vers les classes dérivées. Activation : d’un constructeur par défaut (qui doit exister) ou bien appel à un constructeur explicite. class Z : public Y { public: Z() { cout << ‘z’;} }; int main() { Z z; cout << endl; } L’héritage

27  Construction des objets class X{ public: int i; X(int n) { i = n;} }; class Y : public X { public: int j; Y(int n): X(n) { j = n;} }; S’il n’y a pas de constructeur par défaut spécifié par le programmeur, il faut alors une séquence d’initialisation!!! class Z : public Y { public: int k; Z(int n): Y(n) { k = n;} }; L’héritage

28  Destruction des objets class X{ public: ~X() { cout << ‘X’;} }; class Y : public X { public: ~Y() {cout << ‘Y’;} }; Lors de la destruction d’un objet, les destructeurs de toute la hiérarchie des classes sont appelées dans L’ordre de la hiérarchie « bottom-up » : les destructeurs sont activés de la classe dérivée vesr la racine de l’arbre d’héritage. class Z : public Y { public: ~Z() {cout << ‘Z’;} }; int main() { Z z; cout << endl; } L’héritage

29  Fonctions surchargées L’héritage class M {public : void f(void); void g(void); private :... Protected : }; class D1: {public : void f(void); void h(void); private: Caractéristiques supplémentaires }; class D2: {public : void f(void); void k(void); private: Caractéristiques supplémentaires }; public M

30  Fonctions surchargées (liaison statique) L’héritage class M {public : void f(void); void g(void); private :... Protected : }; D1 w1; D2 w2; w1.f( ); w2.g( ); Vision utilisateur class D1: {public : void f(void); void h(void);...}; public M class D2: {public : void f(void); void k(void); …}; public M Liaison statique Comment dans une hiérarchie par héritage, le compilateur sélectionne la méthode polymorphe à exécuter parmi les surcharges incluses dans la hiérarchie de classes ?

31  Fonctions surchargées. Liaison statique L’héritage Résolution de la surcharge par liaison statique D1 w1; D2 w2; w1.f( ); w2.g( ); Vision utilisateur Classe M Classe D1 Classe D2 Le type de W1 est D1, par conséquent le compilateur regarde si la méthode f est dans la classe D1. Si oui, celle-ci est exécutée. Si non, il la recherche dans la première classe ascendante, etc...

32  Compatibilité de type L’héritage class M {... }; class D: {... }; M* ptrM; D d; ptrM=&d; D* ptrD; M m; ptrD=&m; On dit que le type statique(déclaré) de *ptrM est M. On dit que le type dynamique (réel) de *ptrM est D. L’objet pointé par ptrM reste de type D. Vision utilisateur public M

33  Liaison statique des fonctions L’héritage class figure { public : void tracer (void) { } //action vide }; class rectangle: { public : void tracer (void); //rectangle IC est tracé }; public figure figure* Ptr; Ptr = new rectangle(); Ptr -> tracer( ); Vision utilisateur Le type statique de *Ptr est figure. Par conséquent, l’instruction Ptr->tracer( ) active la méthode tracer de la classe figure. => Action vide

34  Polymorphisme L’héritage  La liaison dynamique est un mécanisme distinct (bien qu'étroitement lié) de l'héritage. C'est le fait qu'un nom de fonction membre d'une classe de base (classe mère) peut être associé à une fonction membre d'une classe dérivée.  Ce mécanisme est essentiel dans la POO. Il permet de reléguer la réalisation d'une fonction membre à un descendant. Le polymorphisme : grâce à la liaison dynamique, un même nom de fonction pourra correspondre à des réalisations différentes suivant les classes dérivées. Le polymorphisme offre la possibilité d’associer à une méthode un code différent selon l’objet auquel elle appartient. Exemples : f ou tracer pour les formes géométriques

35  Liaison dynamique class X{ public: virtual void a() { cout << ‘X’ << endl;} }; class Y : public X { public: virtual void a() { cout << ‘Y’ << endl;} }; C’est la possibilité donc de ne lier qu’au moment de l’exécution la méthode à invoquer (pas possible pour les constructeurs). X x; Y y; x.a(); y.a(); x = y; x.a();//? X *x = new X(); Y *y = new Y(); X->a(); Y->a(); x = y; X->a();//? L’héritage

36  Liaison dynamique. Fonctions virtuelles L’héritage class figure { public : void tracer (void) { } //action vide }; class rectangle : public figure { public : void tracer (void); // instance courante est tracée }; figure* Ptr; Ptr = new rectangle( ); Ptr -> tracer( ); Vision utilisateur La fonction tracer est virtuelle. Par conséquent, l’instruction Ptr->tracer( ); active la méthode associée au type dynamique de *ptr. => Tracé du rectangle virtual

37  Fonctions virtuelles L’héritage  Lors de la redéfinition d’une fonction virtuelle  Les fonctions virtuelles doivent avoir la même liste de paramètres.  Les types de retour sont égaux ou sont des pointeurs compatibles.  L’exécution des fonctions virtuelles s’appuie sur une indirection (table de pointeurs de fonctions virtuelles).

38  Fonctions virtuelles L’héritage  Classes dérivées  Tout comme une fonction ordinaire une fonction virtuelle sert de fonction par défaut dans les classes dérivées.  Une fonction virtuelle redéfinie dans une classe dérivée reste virtuelle dans la classe dérivée même si cela n’est pas spécifié.

39  Liaison dynamique class X{ public: void a() { < cout << ‘X’ << endl;} }; class Y : public X { public: void a() { cout << ‘Y’ << endl;} }; Que se passe-t-il sans « virtual » ? X *x = new X(); Y *y = new Y(); X->a(); Y->a(); x = y; X->a();//? L’héritage

40  Classe abstraite L’héritage Une classe abstraite est une classe qui ne peut pas être instanciée. Exemple : l’instanciation de la classe figure n’a pas de sens. class figure { public : void tracer(void) ; }; Une classe abstraite est une classe qui contient au moins une méthode virtuelle pure, ou qui n'a pas redéfinit une méthode virtuelle pure. virtual =0 //méthode virtuelle pure Une méthode virtuelle pure est une méthode virtuelle dont le corps est explicitement non donné, on précise un =0 a la fin du prototype d'une telle méthode.

41  Méthode virtuelle pure L’héritage  Une classe abstraite est une classe dont aucun objet de ce type ne peut être créé. Ainsi l'abstraction de la classe se propagera dans les classes dérivées tant que la(les) méthode(s) virtuelle(s) pure(s) n'auront pas été redéfinie(s). Cela est relativement pratique dans le cas d'une classe définissant un concept général, et non une classe en elle même.  Lorsqu'une classe définit une méthode virtuelle, le destructeur (s'il est défini) doit être obligatoirement virtuel (sinon on risque de n'appeler que le destructeur de la classe mère alors qu'il s'agit d'un objet de la classe fille).  Attention !! Un constructeur ne peut pas être virtuel et il ne peut pas appeler de méthode virtuelle.

42  Classe abstraite class X{ public: virtual void a() = 0; virtual void b() = 0; … }; class Y : public X { public: virtual void a() { cout << ‘a’ << endl;} virtual void b() { cout << ‘b’ << endl;} }; Classe abstraire Classe non abstraire virtual est facultatif L’héritage

43  Utilisation d’une classe abstraite L’héritage class figure { public : virtual void tracer (void)=0; //fonction virtuelle pure }; figure F; figure* Ptr; Ptr = new figure; Vision utilisateur //illégal // légal

44  Héritage multiple class X{ public: int i; X(int n) { i = n;} void a() {…}; }; class Y { public: int i; Y(int n) { i=n;} void a() {…} }; class Z { public: int j; Z(int n) { j=n;} void b() {…} }; class T: public X, public Y, public Z { public: int i; //redéfinition de i!! T(): X(0), Y(0), Z(0) { i =0;} void c() { j +=1; i +=1; b(); //pas d’ambiguité //a(); //ambiguité X::a(); Y::a(); } }; L’héritage

45  Héritage multiple class X{ public: int i; X(int n) { i = n;} void a() {…}; }; class Y: public X { public: int j; Y(int n) { j=n;} void b() {…} }; class Z: public X { public: int k; Z(int n) { k=n;} void c() {…} }; class T: public Y, public Z { public: int l; T(): X(0), Y(0), Z(0) { l = 0;} void d() { j +=1; k +=1; l +=1; //i +=1; //ambiguité Y::i +=1; Z::i +=1; //a(); //ambiguité Y::a(); Z::a(); } }; L’héritage

46  Héritage multiple class X{ public: int i; X(int n) { i = n;} void a() {…}; }; class Y: virtual public X { public: int j; Y(int n) { j=n;} void b() {…} }; class Z: virtual public X { public: int k; Z(int n) { k=n;} void c() {…} }; class T: virtual public Y, virtual public Z { public: int l; T(): X(0), Y(0), Z(0) { l = 0;} void d() { j +=1; k +=1; l +=1; i +=1; a(); } }; L’héritage

47 Résumé et synthèse Héritage Une classe B peut dériver d’une classe mère A : tout objet de type B hérite alors des attributs et fonctions membres d’un objet parent. En C++, l’héritage peut être multiple : une classe fille peut avoir plusieurs classes mères. Règles d’héritage Constructeurs jamais hérités Méthodes héritées peuvent être redéfinies (overriding) : la nouvelle méthode remplace celle de la superclasse ! ne pas confondre surcharge et redéfinition ! Variables héritées peuvent être surajoutées (shadowing) : la nouvelle variable cache celle de la superclasse ! à éviter : source de confusions !

48 Accès à la classe mère Comme pour les membres, une classe mère A peut être dérivée de manière public, protected ou private : class X: public A {... } ; // X est un sous-type de A. class Y: protected A {... } ; // Héritage d’implémentation: Y et Z class Z: private A {... } ; // restreignent l’interface de A. – X: public A : tous les membres publics et protégés de A peuvent être utilisés par n’importe quelle fonction manipulant un X. C’est la dérivation la plus commune : “X est un A”. Tout objet de type X* peut être converti en A*. – Y: protected A : les membres publics et protégés de A ne peuvent être utilisés que par les fonctions membres et amies de Y ou des classes YY qui dérivent de Y. Seules ces fonctions peuvent convertir un objet de type Y* en A*. – Z: private A : les membres publics et protégés de A ne peuvent être utilisés que par les fonctions membres et amies de Z. Seules ces fonctions peuvent convertir un objet de type Y* en A*. Résumé et synthèse

49 Règles d’héritage Constructeurs jamais hérités Méthodes héritées peuvent être redéfinies (overriding) : la nouvelle méthode remplace celle de la superclasse ! ne pas confondre surcharge et redéfinition ! Variables héritées peuvent être surajoutées (shadowing) : la nouvelle variable cache celle de la superclasse! à éviter : source de confusions ! Résumé et synthèse

50 Fonctions membres virtuelles Une classe dérivée peut remplacer les fonctions membres de sa classe mère. En C++, on parle de membre virtuel : virtual. class A { public: int zero() { return 0 ; } virtual int un() { return 1 ; } }; class B: public A { public: int zero() { return 1 ; } int un() { return 0 ; } }; int main() { A a ; B b ; A &ref_A = a ; ref_A.zero() ; // retourne 0 ref_A.un() ; // retourne 1 A *pt_A ; pt_A = &b ; // possible car la dérivation est publique. pt_A->zero() ; // retourne 0: la fonction est prise dans A car // pt_A est un pointeur sur un objet de type A et zero n’est pas //virtuelle. pt_A->un() ; // retourne 0: la fonction est virtuelle et prise // dans B car pt_A pointe en fait sur un B. pt_A = &a ; pt_A->zero() ; // retourne 0. pt_A->un() ; // retourne 1. return 0; } Résumé et synthèse

51 Polymorphisme par sous-typage L’idée est de partir d’un type et de le modifier. Par exemple, en C++ on peut créer une classe de base, puis faire des classes dérivées. class Forme { public: virtual float Aire() = 0; }; class Carre : public Forme { public: Carre(int a){ m_cote = a;} virtual float Aire() { return m_cote*m_cote; } private:float m_cote; }; class Cercle : public Forme { public:Cercle(int r){ m_rayon = r;} virtual float Aire() { return *m_rayon*m_rayon; } private:float m_rayon; }; Résumé et synthèse

52 Polymorphisme par sous-typage Grâce aux fonctions virtuelles, on peut faire un algorithme en n’utilisant que la classe de base qui va automatiquement appeler les fonctions des classes dérivées: float AireTotale(Forme** tabl, int nb) { float s=0; for(int i = 0; iAire(); // par la table des fonctions virtuelle } return s; }... Forme* tableau[3] = {new Carre(2), new Cercle(1), new Carre(2)}; cout << AireTotale(tableau,3); // on aura En proposant d’utiliser un même nom de méthode pour plusieurs types d’objets différents, le polymorphisme permet une programmation beaucoup plus générique. Le développeur n’a pas à savoir, lorsqu’il programme une méthode, le type précis de l’objet sur lequel la méthode va s’appliquer. Il lui suffit de savoir que cet objet implémentera la méthode. Résumé et synthèse

53 Construction de l’objet parent Les constructeurs d’une classe mère ne sont pas hérités. Lors de la construction d’un objet d’une classe dérivé, il est possible de construire l’objet parent en appelant le constructeur de la classe mère. C’est la seule façon de construire les attributs hérités. class A { private: int a ; public: A( int n ) { a = n ; } }; class B: public A { public: B( int n ) : A(n) { /*...*/ } }; Résumé et synthèse

54 Fonctions virtuelles pures Une classe mère peut définir une fonction membre sans en donner d’implémentation: cette implémentation est laissée aux classes filles. On parle alors de fonction virtuelle pure. class A { public: virtual void f (int i) = 0 ; // f est virtuelle pure. }; class B: public A { public: void f ( int i) { /* Implémentation de f */ } }; Une classe dont un membre au moins est virtuel pur est dite abstraite ; on ne peut Créer d’objets de cette classe (seulement des pointeurs peuvent être manipulés). Une classe dont tous les membres sont virtuels purs sert à définir une interface. Résumé et synthèse

55 Héritage multiple Une classe peut hériter de plusieurs classes. La gestion des ambiguïtés se fait explicitement en utilisant :: ou en indiquant les classe mères moins prioritaires par virtual. Dans le cas d’une telle dérivation virtuelle, les attributs hérités d’une même classe mère virtuelle suivant deux branches d’héritage distinctes ne sont pas dupliqués. Le constructeur d’une classe mère virtuelle est appelé une seule fois lors de la construction d’un objet dérivé (il n’y a qu’un objet de la classe mère). class A { public: int a; //... }; class B: public virtual A { //... }; class C: public virtual A { //... }; class D: public virtual B, public virtual C { // L’objet n’a qu’un objet parent de type A (donc un seul attribut a) } Résumé et synthèse


Télécharger ppt "Structures de données IFT-2000 Abder Alikacem L’héritage en C++ Département d’informatique et de génie logiciel Édition Septembre 2009."

Présentations similaires


Annonces Google