• Aucun résultat trouvé

12. L'HÉRITAGE EN LANGAGE C++

12.6 CLASSE VIRTUELLE .1 Définition

class Fille : public Mere {public:

Fille(void);

~Fille(void);

};

// Le constructeur de la classe fille avec appel du constructeur de la classe mère Fille::Fille(void) : Mere(2)

{cout << "Constructeur de la classe fille" << endl ; return;

}

// Le destructeur de la classe fille appelle celui de la classe mère Fille::~Fille(void)

{cout << "Destructeur de la classe fille " << endl;

return;

}

int main() {Mere A(1);

Fille B;

Mere C(5);

}

// Résultat

Constructeur de la classe mère : m_i = 1 Constructeur de la classe mère : m_i = 2 Constructeur de la classe fille

Constructeur de la classe mère : m_i = 5 Destructeur de la classe mère

Destructeur de la classe fille Destructeur de la classe mère Destructeur de la classe mère

12.6 CLASSE VIRTUELLE 12.6.1 Définition

Position du problème

Soit la classe D héritant de deux classes mères B et C, filles d'une classe mère commune A selon l'arbre

"généalogique" suivant :

153

A

D C B

Selon les qualifications des héritages, les classes B et C peuvent hériter des données et des méthodes publiques et protégées de A, la classe D peut hériter des données de B et C, donc par transitivité des données héritables de A, l'héritage étant implémenté par aggrégat de(s) la structure(s) de données de(s) la classe(s) de base dans celle de la classe dérivée.

Se pose alors le problème suivant : dans la classe D, les données utilisées quand les champs de A sont référencés sont-elles héritées de B ou de C ? Le chemin dans la hiérarchie de classe peut être spécifié par l'opérateur de résolution de portée ce qui n'est ni pratique ni toujours efficace.

Définition

Le problème est résolu en déclarant virtuelle la classe de base commune dans la définition de l'héritage des classes filles avec l'effet suivant : le compilateur garantissant l'unicité des données héritées que l'héritage soit simple ou multiple, les données membres de la classe de base virtuelle ne peuvent pas être dupliquées dans les classes dérivées à partir du deuxième ordre.

Syntaxe

L'identificateur de la classe mère est précédé du qualificateur virtual dans la définition des classes filles.

Exemple class A {protected:

int Donnee; // La donnée de la classe de base };

class B : virtual public A // Héritage de la classe virtuelle A {protected:

int Valeur_B;

};

class C : virtual public A // A est toujours virtuelle {protected:

int valeur_C; // nouveau champ, Donnee est acquise par héritage.

154

};

class D : public B, public C // le champ Donnee est garanti unique.

{}; // La classe D

Exercice : analyser le programme suivant

#include <iostream.h>

class mere1 {int m1;

public:

mere1(){}

mere1(int i) {cout << "constructeur mère 1 "<< endl; m1=i;}

void affiche() {cout<< "m1=" << m1 << endl;}

};

class mere2 {int m2;

public:

mere2(){}

mere2(int i) {cout << "constructeur mère 2 "<< endl; m2=i;}

void affiche() {cout<< "m2=" << m2<< endl;}

};

class fille1 : virtual mere1, virtual mere2 {public :

fille1(){}

fille1(int i1,int i2): mere1(i1),mere2(i2) {cout << "constructeur fille1" << endl;}

void affiche() {mere1::affiche(); mere2::affiche();}

};

class fille2 : virtual mere1, virtual mere2 {public :

fille2(){}

fille2(int i1,int i2): mere1(i1),mere2(i2) {cout << "constructeur fille2" << endl;}

void affiche() {mere1::affiche(); mere2::affiche();}

};

class petitefille : fille1,fille2 {public:

petitefille(int i1,int i2): mere1(i1),mere2(i2),fille1(i1,i2) ,fille2(i1,i2) {cout << "constructeur petitefille" << endl;}

void affiche() {fille1::affiche();}

};

155

int main()

{petitefille pf(4,5) ; pf.affiche();

}

// résultat

constructeur mère 1 constructeur mère 2 constructeur fille1 constructeur fille2 constructeur petitefille m1=4

m2=5

Exercice : analyser le programme suivant. Différences avec le précédent

#include <iostream.h>

class mere1 {int m1;

public:

mere1(){}

mere1(int i) {cout << "constructeur mère 1 "<< endl; m1=i;}

void affiche() {cout<< "m1=" << m1 << endl;}

};

class mere2 {int m2;

public:

mere2(){}

mere2(int i2) {cout << "constructeur mère 2 "<< endl; m2=i2;}

void affiche() {cout<< "m2=" << m2<< endl;}

};

class fille1 : mere1, mere2 {public :

fille1(){}

fille1(int i1,int i2): mere1(i1),mere2(i2) {cout << "constructeur fille1" << endl;}

void affiche() {mere1::affiche(); mere2::affiche();}

};

class fille2 : virtual mere1, virtual mere2 {public :

fille2(){}

fille2(int i1,int i2): mere1(i1),mere2(i2) {cout << "constructeur fille2" << endl;}

156

void affiche() {mere1::affiche(); mere2::affiche();}

};

class petitefille : fille1,fille2 {public:

petitefille(int i1,int i2): fille1(i1,i2) ,fille2(i1,i2) {cout << "constructeur petitefille" << endl;}

void affiche() {fille1::affiche();}

};

int main()

{petitefille pf(4,5) ; pf.affiche();

}

// résultat

constructeur mère 1 constructeur mère 2 constructeur fille1 constructeur fille2 constructeur petitefille m1=4

m2=5

12.6.2 Règles d'utilisation des classes virtuelles

 L'utilisation de l'opérateur de transtypage dynamique dynamic_cast est impérative pour transtyper un pointeur sur un objet d'une classe de base virtuelle en un pointeur sur un objet d'une de ses classes dérivées.

 Les classes dérivées au deuxième ordre ou plus, pouvant dériver d'une même classe virtuelle, ne peuvent pas utiliser leur(s) classe(s) mère pour appeler le constructeur de la classe virtuelle, ce dernier pouvant être appelé plusieurs fois. Chaque classe dérivée prend donc en charge la construction des objets des classes virtuelles dont elle hérite.

 Un constructeur d'une classe de base virtuelle doit être appelés explicitement par toute classe dérivée quel que soit son ordre, les données de la classe de base virtuelle y étant garanties uniques.

 Le constructeur de toute classe dérivée d'une classe virtuelle, quel que soit son ordre, doit en appeler explicitement le constructeur pour éviter l'appel du constructeur par défaut même si un constructeur spécifique est défini dans une autre classe mère dérivée de la classe de base virtuelle, chacune des classes mère pouvant initialiser un objet commun hérité de la classe virtuelle. Dans l'exemple ci-dessus, si les classes B et C appellent toutes deux le constructeur de la classe virtuelle A, et que la classe D utilise les constructeurs de B et C, l'objet hérité de A est construit plusieurs fois. Pour l'éviter, le compilateur ignore purement et simplement les appels implicites au constructeur des classes de bases virtuelles dans les classes de base dérivées qui doivent donc être systématiquement spécifié, à chaque niveau de la hiérarchie des classes.

157