Академический Документы
Профессиональный Документы
Культура Документы
2018/2019
Chapitre 4
1 ATEL
L’héritage appelé aussi dérivation est un principe de la POO
permettant de créer une nouvelle classe à partir d’une classe
existante tout en profitant des attributs et des méthodes de cette
dernière et en définissant de nouveaux attributs et de nouvelles
méthodes propres à la classe dérivée. Par cette méthode on crée
une hiérarchie de classes de plus en plus spécialisées. Cela a un
avantage majeur de ne pas partir toujours de zéro dans la
conception d’applications.
La hiérarchie des classes constituée est aussi appelée
arborescence des classes. Au niveau de chaque nœud de
l’arborescence existe une relation de parenté. Le nœud est appelé
classe de base, ou classe parent, ou classe ancêtre, ou classe père
ou classe mère… et les classes dérivées sont appelés classes filles
ou sous-classes.
2
Cette notion pose bien entendu un certain nombre de questions
auxquelles il va falloir répondre :
3
Mode de
dérivation
Classe
mère
Classe
dérivée La classe B hérite de façon publique de
la classe A. Tous les membres publics ou
protégés de la classe A font partie de
l’interface de la classe B. 4
Le mode de dérivation, spécifié par un des mots public,
protected ou bien private, permet d’indiquer la protection des
données de la classe de base dans la dérivation :
5
Le mode de dérivation constitue donc une sorte de filtre dans le processus de
dérivation :
Les données seront ou non accessibles par la classe fille et les classes
petites filles.
Les membres privés ne sont jamais accessibles par les membres de la
classe dérivée.
Le mode de dérivation ne concerne donc que les membres protected ou
public.
En mode de dérivation public, le statut d’un membre reste inchangé : un
membre public reste public et un membre protected reste protected.
En mode de dérivation protected, le statut d’un membre devient protected.
En mode de dérivation private, le statut d’un membre devient private. C’est
à dire qu’il sera considéré dans la classe dérivée comme un membre private
donc inaccessible par les classes petites filles.
6
Sile mode de dérivation n’est pas spécifié, le compilateur prend par
défaut le mode private pour une classe et public pour une structure.
Le tableau suivant résume le statut des membres selon le mode de
dérivation :
7
C’est le mode d’héritage le plus courant. Il donne aux membres public et
protected, le même statut dans la classe dérivée.
Exemple
8
Le mode protected donne aux membres public et protected, le statut protected
dans la classe dérivée.
Exemple
9
Le mode private donne aux membres public et protected, le statut private
dans la classe dérivée.
Exemple
10
On peut redéfinir une fonction dans une classe
dérivée si on lui donne le même nom que dans la
classe de base. Il y aura ainsi deux fonctions de
même nom dans un objet, mais il sera possible de
les différencier en utilisant l’opérateur de résolution
de portée ::
Si A est la classe de base, B la classe dérivée et
« fonc » le nom de la fonction redéfinie :
12
Exemple 1
Ce programme affiche:
Const. de la classe A
Const. de la classe B
Dest. de la classe B
Dest. de la classe A
int
13
Exemple 2 : Appel du constructeur avec paramètres
int
14
L’amitié ne se propage pas aux membres de la
classe dérivée et ne s’étend pas aux générations
suivantes.
15
Conversion de variable
16
Exemple void traitement1(Vehicule v)
{ // ...
class Vehicule v.f1(); // OK
{ public: // …
void f1(); }
// ... int main()
}; {
class Voiture : public Voiture R25;
Vehicule traitement1( R25 );
{ public: // R25 est automatiquement
int f1(); //converti en une instance
}; de la //classe Vehicule
}
17
Conversion de pointeur
18
Exemple
C’est le type du pointeur qui détermine laquelle des
méthodes f1( ) est appelée
void traitement1(Vehicule *v)
{
// ...
v->f1( ); // OK
// ...
}
int main()
{ Voiture R25;
traitement1( &R25 );
}
19
En langage C++, il est possible d’utiliser l’héritage multiple :
Une classe peut hériter de deux ou plusieurs classes. Pour
chaque classe de base, on peut définir le mode d’héritage.
20
Exemple
class C: public B, public A
class A { int c;
{ public: public:
void fa() { /* ... */ } void fc();
protected: };
int a;
};
class B void C::fc()
{ public: { int i;
void fb() { /* ... */ } fa( );
protected: i = a + b;
int b; }
};
21
Ordre d’appel des constructeurs
22
Exemple class C: public B, public A
class A // ordre d’appel des constructeurs
{ public: { public:
A(int n=0) { /* ... */ } C(int i, int j) : A(i) , B(j)
// ... { /* ... */ }
}; // liste d’initialisation
class B // ...
{ public: };
B(int n=0) { /* ... */ } int main()
// ... { C objet_c;
}; // appel des constructeurs
//B(), A() et C()
// ...
}
Les destructeurs sont appelés dans l’ordre inverse de celui des constructeurs.
23
Un objet de la classe D contiendra deux fois les données héritées de la
classe de base A, une fois par héritage de la classe B et une autre fois
par C. Il y a donc deux fois le membre « base » dans la classe D. L’accès
au membre « base » de la classe A doit se faire en levant l’ambiguïté
24
int main()
{ D od;
od.base = 0; // ERREUR, ambiguïté
od.B::base = 1; // OK
od.C::base = 2; // OK
}
25
Il ne faut pas confondre ce statut
Exemple "virtual" de déclaration
int main() d’héritage des classes avec celui
{ D od; des membres virtuels que nous
od.base = 0; // OK, pas d’ambiguïté allons étudier. Ici, le mot-clé
} virtual précise au compilateur les
classes à ne pas dupliquer
26
Le polymorphisme permet l’utilisation d’une
même instruction pour appeler dynamiquement
des méthodes différentes dans la hiérarchie des
classes.
En C++, le polymorphisme est mis en œuvre
par l’utilisation des fonctions virtuelles.
27
Fonctions virtuelles
Exemple void traitement (Graphe &og)
{ // ...
class Graphe og.print();
{ public: // ...}
void print()
{ cout <<"Graphe::print()"; }
}; int main()
class Bouton: public Graphe {
{ public: // Qu’affiche ce programme ???
void print() Graphe X;
{ cout << "Bouton::print()"; } Bouton OK;
}; Fenetre Windows97;
class Fenetre: public Graphe traitement (X);
{ public: traitement (OK);
void print() traitement(Windows97);
{ cout << "Fenetre::print()"; }
}; }
28
Comme nous l’avons déjà vu, l’instruction og.print() de
traitement() appellera la méthode print() de la classe
Graphe car il va y avoir une conversion automatique des
deux objets en objet de type Graphe. La réponse est donc :
29
Si dans la fonction traitement() nous voulons appeler la méthode
print() selon la classe à laquelle appartient l’objet, nous devons
définir, dans la classe de base, la méthode print() comme étant
virtuelle (C’est ça le polymorphisme) :
class Graphe
{ public:
// ...
virtual void print() const
{ cout<< "ObjetGraphique::print()" << endl;}
};
30
Pour plus de clarté, le mot-clé virtual peut être répété devant les
méthodes print() des classes Bouton et Fenetre :
class Bouton: public Graphe
{
public:
virtual void print() const
{ cout << "Bouton::print()"; }
};
class Fenetre: public Graphe
{
public:
virtual void print() const
{ cout << "Fenetre::print()"; }
};
31
On appelle ce comportement, le polymorphisme. Lorsque le
compilateur rencontre une méthode virtuelle, il sait qu’il faut
attendre l’exécution pour déterminer la bonne méthode à
appeler.
Le programme affichera :
32
Destructeur virtuel
class Graphe
{ public:
//...
virtual ~Graphe() { cout << "fin de Graphe\n"; }
};
class Fenetre : public Graphe
{ public:
// ...
~Fenetre( ) { cout << "fin de Fenêtre "; }
};
33
int main()
{ Fenetre *Windows97 = new Fenetre;
Graphe *og = Windows97;
// ...
delete og;
// affichage de : fin de Fenêtre fin de Graphe
// si le destructeur n’avait pas été virtuel,
// l’affichage aurait été : fin de Graphe
}
Un constructeur, par contre, ne peut pas être déclaré comme
virtuel. Une méthode statique ne peut, non plus, être déclaré
comme virtuelle. Lors de l’héritage, le statut de l’accessibilité
de la méthode virtuelle (public, protégé ou privé) est conservé
dans toutes les classes dérivées, même si elle est redéfinie
avec un statut différent. Le statut de la classe de base prime.
34
Une méthode virtuelle pure est une méthode qui est déclarée
mais non définie dans une classe. Elle est définie dans une des
classes dérivées de cette classe. Une telle méthode se déclare
en ajoutant un = 0 à la fin de sa déclaration.
Exemple
class Graphe
{
public:
virtual void print() =0; //pas d’instance
//de cette méthode
}; // dans cette classe.
35
int main()
{ Graphe og; // ERREUR
// ...
}
Contrairement à une méthode virtuelle "normale", une méthode
virtuelle pure n’est pas obligé de fournir une définition pour
Graphe::print().
36
Il arrive souvent que la méthode virtuelle définie dans la
classe de base sert de cadre générique pour les méthodes
virtuelles des classes dérivées. Ceci permet de garantir
une bonne homogénéité de votre architecture de classes.
Une classe est dite abstraite si elle contient au moins une
méthode virtuelle pure.
Étant donné que les classes abstraites ont des méthodes
non définies, il est impossible d’instancier des objets pour
ces classes. En revanche, on pourra les référencer avec
des pointeurs.
37
Le mécanisme des méthodes virtuelles pures et des classes abstraites
permet de créer des classes de base contenant toutes les
caractéristiques d’un ensemble de classes dérivées, pour pouvoir les
manipuler avec un unique type de pointeur. En effet, les pointeurs des
classes dérivées sont compatibles avec les pointeurs des classes de
base, on pourra donc référencer les classes dérivées avec des
pointeurs sur les classes de base, donc avec un unique type sous-
jacent : celui de la classe de base.
Cependant, les méthodes des classes dérivées doivent exister dans la
classe de base pour pouvoir être accessibles à travers le pointeur sur la
classe de base. C’est ici que les méthodes virtuelles pures
apparaissent. Elles forment un moule pour les méthodes des classes
dérivées, qui les définissent. Bien entendu, il faut que ces méthodes
soient déclarées virtuelles, puisque l’accès se fait avec un pointeur de
la classe de base et qu’il faut que ce soit la méthode de la classe réelle
de l’objet (c’est à dire la classe dérivée) qui soit appelée.
38
Exemple
int main()
{
Graphe *og1, *og2, *TabMixte[100];
og1= new Bouton;
og2= new Fenetre;
og1->print();
og2->print ();
TabMixte[0] = new Bouton;
TabMixte[1] = new Fenetre;
//Etc…
}
39