6

Click here to load reader

Ch10

Embed Size (px)

Citation preview

Page 1: Ch10

Chapitre X Héritage multiple

• On parle d'héritage multiple lorsqu'une classe dérivée admet plusieurs classes de base directes.

• L'héritage multiple est une généralisation de l'héritage simple. La plupart des résultats et les notions mentionnés dans l'héritage simple restent valables pour l'héritage multiple (En effet si T dérive de deux classes A et B, alors T est une classe dérivée de A (resp de B) dans le sens de l'héritage simple). Cependant, quelques problèmes qui n'existaient pas dans l'héritage simple apparaissent :

♦ Conflits entre identificateurs des membres hérités ♦ Duplication des membres ♦ Conflits d'initialisations des constructeurs des différentes classes de base

X.1 Un exemple d'héritage multiple • Notons premièrement que si une classe T hérite de plusieurs classes A, B, …

class T : mode_a A, mode_b B,…

{… …};

où mode_a (mode_b, …) désigne le mode d'héritage (public, protégé ou public)

Page 2: Ch10

Héritage multiple 100

le constructeur de T doit fournir les paramètres nécessaires à tous les constructeurs de ses classes de base. Pour cela on utilise la syntaxe suivante:

T::T(… …) : A(…), B(…),…

De plus, avant tout appel du constructeur de T, il y aura appel des constructeurs de classes de base suivant l'ordre de la liste des classes de base qui figure dans l'entête de T. L'appel des destructeurs se fait dans l'ordre inverse.

Exemple 10.1 : (EXP10_01.CPP)

L'exemple suivant montre le cas d'un héritage multiple. On y définit deux classes point et couleur, et une classe pointcol qui dérive de ces deux classes :

#include <iostream> using namespace std; // ------------------- Classes de base class point{ protected: int x; int y; public: point(int, int); ~point(); void affiche_point(); }; point::point(int abs, int ord){ x = abs; y = ord; cout << "--- constr de point" << endl; } point::~point(){ cout << "--- destr de point " << endl; } void point::affiche_point(){ cout << "(" << x << "," << y << ")"; } class couleur{ protected: unsigned int _couleur; public: couleur(unsigned int); ~couleur(); void affiche_couleur(); }; couleur::couleur(unsigned int c){ _couleur = c; cout << "--- constr de couleur" << endl; } couleur::~couleur(){ cout << "--- destr de couleur " << endl; } void couleur::affiche_couleur(){ cout << "(" << _couleur << ")"; } // ------------------- Classe derivée class pointcol: public point, public couleur { public: pointcol(int, int, unsigned int);

Page 3: Ch10

Héritage multiple 101

~pointcol(); }; pointcol::pointcol(int abs, int ord, unsigned int c):point(abs,ord), couleur(c) { cout << "--- constr de pointcol" << endl; } pointcol::~pointcol(){ cout << "--- destr de pointcol" << endl; } //--------------------- TEST int main() { pointcol a(1,2,3); a.affiche_point(); cout << " - "; a.affiche_couleur(); cout << endl; return 0; }

► Sortie du programme --- constr de point --- constr de couleur --- constr de pointcol (1,2) - (3) --- destr de pointcol --- destr de couleur --- destr de point

Notez l'ordre d'appel des constructeurs et des destructeurs.

X.2 Résolution des conflits entre identificateurs • Etant donné une classe T qui hérite de deux classes A et B. Si un membre de B possède

le même identificateur que celui d'un membre de A, alors T possèdera deux membres différents qui portent le même nom et tout accès à l'un de ces deux membres sera ambiguë. Pour résoudre cette ambiguïté il faut utiliser l'opérateur de résolution de portée :

class A {… float x; …} class B {… int x; …} class T: public A, public B {… …} T u; u.A::x; // fait référence au réel x défini dans A u.B::x; // fait référence à l'entier x défini dans B u.x // rejeté, ambiguïté

Exemple 10.2 : (EXP10_02.CPP)

Cet exemple reprend l'exemple précédent dans lequel on renomme les fonctions membres affiche_point() et affiche_couleur() en affiche(). Dans ce cas le programme test devient :

Page 4: Ch10

Héritage multiple 102

int main() { pointcol a(1,2,3); a.point::affiche(); cout << " - "; a.couleur::affiche(); cout << endl; return 0; }

X.3 Classes virtuelles • Supposons qu'une classe T hérite de deux classes A et B et que ces deux classes

héritent d'une autre classe C :

T hérite donc deux fois de C par l'intermédiaire de A et de B. Autrement dit, si A et B hérite de C un membre nommé x, la classe T aura deux membres nommés x, (un hérité de A et l'autre hérité de B) qui désignent la même donnée. Dans ce cas pour accéder à ce membre dans T, on doit spécifier le chemin complet suivant l'arborescence de l'héritage :

T u; u.A::C::x; // fait référence à x hérité de C via A u.B::C::x; // fait référence à x hérité de C via B

Or ceci n'est pratique ni efficace, en plus généralement une seule copie du membre x est suffisante.

• Pour résoudre ce problème, on déclare virtuelle la classe de base commune C dans la spécification de l'héritage de A et B comme suit :

class C {… …}; class A : public virtual C {… …}; class B : public virtual C {… …}; class T: public A, public B {… …}

dans ce cas, le compilateur n'incorpore qu'une seule copie des membres hérités de C dans T et l'accès à ces membres se fera son l'utilisation de l'opérateur de résolution de portée.

• Cependant, il faut noter que :

♦ Le constructeur d'une classe qui hérite directement ou indirectement d'une classe virtuelle doit fournir les paramètres nécessaires au constructeur de cette classe virtuelle. Dans le cas de notre exemple le constructeur de T doit fournir les paramètres nécessaires au constructeur de C comme suit :

C

A

T

B

Page 5: Ch10

Héritage multiple 103

T::T(… …) : C(…), A(…), B(…)

♦ Si un membre de la classe de base virtuelle commune est redéfini dans une des classes dérivées, c'est la définition de la classe dérivée qui prédomine. Si par exemple fct() est une fonction membre de C qui est redéfinie dans B, alors tout appel de cette fonction dans T ou avec une instance de T invoquera B::fct().

Page 6: Ch10

Héritage multiple 104