L'héritage


Sommaire du chapitre :

1. Héritage public de classes

2. Héritage public/privé de classes

3. Propriété private utilisée sélectivement

4. Paramètres d'initialisation

5. Héritage multiple indirect de la même classe

6. Superposition d'héritages multiples


1. Héritage public de classes

class A { public : int Var_A };
class B: public A
{

public
:
A Var_B1 , Var_B2;
int
Var_B3;
}
;

Dans cet exemple la classe B contient trois fois la classe A :
- une fois par héritage
- deux fois par emboîtement selon la technique que nous avons vue précédemment.

Pour hériter publiquement une classe A dans une classe C on ajoute : public A après le nom B.

Le membre hérité n'a pas de nom de champ.
Tous les membres d'une classe héritée font partie de la nouvelle classe.

On peut aussi concevoir que la classe B consiste en une classe A à laquelle on a ajouté le corps de la classe B en appendice.
On verra par la suite que les objets de type B peuvent être manipulés par les objets et les procédures qui ne connaissent que la classe A.
Bien entendu ceux-ci ne peuvent pas accéder au membre Objet.Var_B3 , ni aux membres Objet.Var_B1 et Objet.Var_B2 , ni aux autres membres emboîtés dans B.

Accès aux composantes

B Objet;
Objet.Var_A = 1;
Objet.Var_B1.Var_A = 1;
Objet.Var_B2.Var_A = 1;

Différences entre l'héritage et l'emboitement

- L'héritage n'ajoute pas de nouveau nom de membre local à B.
Si l'on instancie un objet Objet de classe B, on peut référencer le membre Var_A hérité par Objet.Var_A), alors que les deux membres obtenus par emboîtage sont référencés par Objet.Var_B1.Var_A et Objet.VarB2.Var_A.
Contrairement à ce qui se passe avec l'emboîtement, il est impossible d'hériter directement plus d'une fois la même classe dans une autre classe.
On ne pourrait en effet pas identifier les différents héritages.

- La deuxième différence réside dans les possibilité de sous-typage :
l'objet peut être vu comme appartenant à la classe héritière ou à la classe héritée (cf le chapitre sur le sous-typage).

retour au sommaire


2. Héritage public/privé de classes

class MaClasse :
private Classe1,
public Classe2, ...
{
<Déclaration des membres>
};

De façon plus générale, il est possible d'hériter les classes de façon privée ou publique.
Pour hériter plusieurs classes, on place deux points après le nom de la classe qui hérite, puis les noms des classes héritées, séparées par des virgules.
Devant chaque nom il est possible de mettre le mot-clé private ou public.
Si l'on ne met rien, le compilateur considère que la classe est héritée en mode private.

Les membres publics des classes héritées de façon publique sont accessibles normalement depuis l'extérieur, comme les membres publics emboîtés localement.
Pour qu'on puisse atteindre un membre d'une classe héritée, depuis l'extérieur de l'objet, il faut donc que public apparaisse deux fois, une fois devant l'héritage et une fois devant le membre.
Aucun des membres d'une classe héritée de façon privée, n'est accessible de l'extérieur.

retour au sommaire


3. Propriété private utilisée sélectivement

class A
{
public : int x,y ;
protected : int z ;
};
class B :
private A
{
public : A::x ;
protected : A::y ;
protected : A::z ;
};

Considérons une classe A qui contient des éléments publics et d'autres privés.
Cette classe est héritée de façon privée dans une autre classe, B.
Si l'on ne veut faire apparaître aux objets extérieurs qu'un nombre limité des membres de la classe A , on peut les requalifier indépendamment les uns des autres, sans toutefois mettre un accès plus étendu que celui qui était défini à l'origine.
Private ne peut donc être changé dans aucun cas.
Ici, le membre x est remis au mode d'accès public , le membre y est remis à mi-chemin, au mode protected , et le membre z est remis à son mode d'origine, protected.

retour au sommaire


4. Paramètres d'initialisation

Si l'on déclare un objet d'une classe donnée entre les accolades d'une fonction ou même dans une paire d'accolades à l'intérieur d'une fonction, cet objet est créé et son constructeur appelé au moment de la déclaration.
L'objet est détruit et son destructeur appelé lorsque l'on rencontre l'accolade droite.
Ci-dessous, la ligne /* REPERE */ étant définie à l'intérieur d'une fonction, on peut y placer un objet A avec ses paramètres d'initialisation :

class A { A(int); } ;
class B { B(char); } ;
class XXX : private A, public B
{
A x;
int k;
XXX(int i) : B('z') , x(12) , A(i) , k(o);
f() { A x(15); } /* REPERE */
};

Cette liste contient les paramètres qui initialisent les classes héritées et ceux qui initialisent les membres.
L'ordre d'initialisation est donné par cette liste.
Elle a force de précédence sur l'ordre d'héritage.
Toutefois ce sont les classes héritées qui sont initialisées d'abord, puis les membres.
Dans le cas présenté ici, on initialise la classe B puis la classe A , alors qu'elles sont héritées dans l'autre ordre
L'objet x est initialisé en dernier.

Le compilateur annonce une erreur s'il n'y a pas de constructeur sans paramètre pour une variable d'une classe héritée et qu'il n'y a pas d'initialisation sur la ligne du constructeur.

retour au sommaire


5. Héritage multiple indirect de la même classe

Ci-dessous nous supposons avoir deux classes A et B qui héritent toutes les deux de la classe X.
La classe C peut hériter de A et de B. L'objet C contient donc deux fois les objets de classe X.
Pour y accéder, il faut les identifier en faisant précéder le champ par le nom de la classe intermédiaire.

class X { public : int x ; } ;
class A : public X { } ;
class B : public X { } ;
class C : public A, public B
{
A::x = 0 ;
} ;
void main()
{
C Objet;
Objet.A::x = 1 ;
Objet.B::x = 2 ;
}

retour au sommaire


6. Superposition d'héritages multiples

Dans ce paragraphe une première utilisation du mot-clé virtual est expliquée.

class X { public : int x } ;
class A : virtual public X { } ;
class B : virtual public X { } ;
class D : public A, public B
{
x = 0;
} ;
void main()
D Objet ;
Objet.x = 1 ;
} ;

Si l'on ajoute le mot-clé virtual devant l'héritage de X, l'objet X n'apparaît qu'une fois.
Pour accéder à x , il n'y plus besoin de spécifier de quel x il s'agit.

class X { public : int x } ;
class A : virtual public X { } ;
class B : virtual public X { } ;
class C : public X { } ;
class D : public A, public B, public C
{
C::x = 0;
} ;
void main()
D Objet ;
Objet.B::x = 1 ;
Objet.C::x = 1 ;
}
;

Il est possible de faire simultanément des héritages virtuels et d'autres non virtuels, ainsi que le montre l'exemple ci-dessus.
La désignation des deux X différents est faite de façon naturelle.

retour au sommaire
chapitre suivant