1. L’héritage en C++
Philippe d’Anfray GIP RENATER
2006-2007
Philippe.d-Anfray@renater.fr
Formation d’Ing´enieurs de l’Institut Galil´ee MACS 2
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 1/30
Héritage/Définition (1)
On peut dériver une classe à partir d’une autre. La classe dérivée
hérite de toutes les caractéristiques de la classe dite de base.
class BASE class DERIVEE : PUBLIC BASE
{ {
int i, j; int k, l;
... ...
public: public:
void f_base (...); void f_derivee (...);
... ...
}; };
...
On intéressera essentiellement à l’héritage “public”.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 2/30
Héritage/Définition (2)
Une instance de la classe dérivée possède en plus des siens toutes
les données et toutes les méthodes de la classe de base.
...
int main()
{
DERIVEE d1, d2;
d1.f_base (...); // Possible, vient de BASE
d1.f_derivee (..); // OK aussi vient de DERIVE
}
On peut redéfinir le comportement de l’objet en surchargeant les
méthodes
Attention. . . voir plus loin. . .
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 3/30
Héritage/Définition (3)
Le processus peut se répéter, on obtient des graphes d’héritage
Héritage simple: un arbre,
Héritage multiple: graphe acyclique.
Base
Dérivé_2Dérivé_1
Dérivé_3 Dérivé_4
Dérivé_2Dérivé_1 Dérivé_3
Dérivé_4
Base_1 Base_2
Arbre d’héritage (simple) Graphe d’héritage (multiple)
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 4/30
2. Privé/Public (1)
Rappel: lors de la définition des objets, il y a toujours deux points de
vues et donc deux statuts possibles pour les champs:
le programmeur concepteur/développeur, partie “privée”;
le programmeur utilisateur, partie “public”.
Comment ces notions passent-elles à travers les mécanismes
d’héritage ?
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 5/30
Privé/Public (2)
Règle générale:
La partie privée de la classe de base reste inaccessible à la
classe dérivée.
Le concepteur de DERIVEE n’a pas accès à la partie privée de
la classe de BASE sauf si elle est déclarée explicitement amie.
Tout ce qui peut modifier la partie privée d’une classe doit être
déclaré dans le corps de la classe.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 6/30
Privé/Public (3)
Cas de l’héritage “privé”, la partie publique de BASE devient privée
pour DERIVEE. Le concepteur de DERIVEE y a accès pas
l’utilisateur.
class BASE class DERIVEE:BASE
{ {
private://dev. BASE private:// dev. DERIVEE
... ...
public: //util. BASE public: // util. DERIVEE
... //dev. DERIVEE ...
}; };
Mais on s’intéresse plutôt à l’héritage “public”
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 7/30
Privé/Public (4)
La partie publique de BASE peut être rendus accessible aux
utilisateurs de DERIVEE: héritage “public”.
class BASE class DERIVEE:public BASE
{ {
private://dev. BASE private:// dev. DERIVEE
... ...
public: //util. BASE public: // util. DERIVEE
... //dev. DERIVEE ...
... //util. DERIVEE };
... //car public
};
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 8/30
3. Privé/Public (5)
Pour des raisons d’optimisations, le concepteur de DERIVEE a
souvent besoin d’ accèder à la partie privée de BASE.
Cela donne lieu à deux types d’excès:
le tout ami: plus aucune protection;
le tout public: visibilité totale.
Problème: on perd la séparation spécification/réalisation !
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 9/30
Protégé (1)
Il existe un statut intermédiaire: le champ protégé:
class BASE class DERIVEE: public BASE
{ private: //dev. BASE {
... private:
protected://dev. BASE ...
... //dev. DERIVEE public:
public: //util. BASE ...
... //dev. DERIVEE };
};
Un champ protégé de BASE est accessible aux développeurs de
DERIVEE pas aux utilisateurs.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 10/30
Héritage / Fonctions amies (1)
Les relations d’amitié ne se transmettent pas par héritage.
class BASE class DERIVEE: public BASE
{ {
private: ... private:
protected: ... ...
public: public:
friend void ff ...
(BASE& b...); ...
... };
};
ff n’est pas amie de DERIVEE.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 11/30
Héritage/ Constructeurs-Destructeurs (1)
Deux règles simples:
Le constructeur de la classe de base si il existe est appelé avant
le constructeur de la classe dérivée.
Le destructeur de la classe de base si il existe est appelé après
le destructeur de la classe dérivée.
Se généralise si il y a plusieurs dérivations.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 12/30
4. Héritage/ Constructeurs-Destructeurs (2)
Un problème, celui des arguments:
class BASE class DERIVEE : public BASE
{ ... { ...
public: public:
// avec arguments // avec arguments
BASE DERIVEE
(int b_1...float b_n) (float d_1...char* d_m);
{... ...
} };
};
Je dois pouvoir utiliser DERIVEE sans a priori connaître BASE.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 13/30
Héritage/ Constructeurs-Destructeurs (3)
Quand je réalise le constructeur de DERIVEE je dois donner les
valeurs des arguments du constructeur de BASE:
DERIVEE::DERIVEE (float d_1, ...char* d_m)
:BASE (val_b_1...val_b_n);
//
// val_b_1...val_b_n sont les valeurs
// des arguments b_1...b_n passes au
// constructeur de la classe BASE
// pour construire un objet DERIVEE
{
... ;
}
Les destructeurs n’ont pas d’argument, pas de problème(s).
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 14/30
Polymorphisme (1)
Je peux redéfinir une fonction:
class BASE class DER:public BASE int main()
{ { {
... ... DER d;
public: public: // ff de DER
void ff(..); void ff (..); d.ff(..);
... ... // ff de BASE
}; }; d.BASE::ff(..);
}
Notez le come-back de l’opérateur ::
Les redéfinitions de fonctions sont dangereuses à manipuler...
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 15/30
Polymorphisme (2)
Un pointeur sur une instance d’une classe dérivée peut être
considéré comme un pointeur sur une instance de la classe de base.
Dans l’autre sens le typage explicite est obligatoire .
main()
{
BASE *p_base;
DERIVEE *p_derivee;
...
// licite du particulier au general
p_base = p_derivee;
// du general au particulier: requete explicite
p_derivee = (DERIVEE *) p_base;
...
}
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 16/30
5. Fonctions virtuelles (1)
Soit une classe matrice_carree. Je sais définir les méthodes mais
la forme des données n’est pas fixée à priori:
Matrice
Symétrique
Carrée
Matrice Matrice tridiagonale ...
.........
Matrice Pleine
On aura une réalisation différente pour chaque classe dérivée.
notion d’archétype.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 17/30
Fonctions virtuelles (2)
Fonction virtuelles:
class matrice_carree
{
// donnees: pas les elements
public:
virtual inverser(..);
...
}
Ceci signifie que je vais (éventuellement) redéfinir la méthode
inverser dans les classes dérivées.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 18/30
Fonctions virtuelles (3)
class matrice_symetrique: public matrice_carree
{
// donnes privees
public: ...
virtual inverser (...);
};
class matrice_tridiagonale: public matrice_carree
{
// donnees privees
public: ...
virtual inverser (...);
};
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 19/30
Fonctions virtuelles (4)
Fonction virtuelle, plus que la simple surcharge: mécanisme de la
liaison dynamique.
Une application peut utiliser matrice_carree sans connaître a
priori les types qui seront dérivés:
void f_appli (matrice_carree& m, ...)
{
...
m.inverser (); // on travaille sur le
... // type matrice_carree
}
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 20/30
6. Fonctions virtuelles (5)
On ira chercher à l’exécution la réalisation de inverser
correspondant au type réel de l’instance (celui de la déclaration).
int main()
{
matrice_tridiagonale mt;
// ici on utilisera pour "inverser" dans ‘‘f_appli’’
// la realisation de la classe derivee tridiagonale
f_appli (mt, ..);
...
}
Si une fonction doit être redéfinie, utiliser une fonction virtuelle.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 21/30
Fonctions virtuelles (6)
On ira chercher à l’exécution la réalisation de la méthode
correspondant au type réel de l’instance (celui de la déclaration).
C’est pour cette raison que le destructeur doit être virtuel.
N.B. le constructeur ne peut être virtuel.
Attention en cas de “simple surcharge” le choix sera fait à la
compilation.
Problème: l’éventuel surcoût du mécanisme (compilateurs
“dévirtualiseurs”).
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 22/30
Classe abstraite (1)
Une fonction virtuelle pure ne reçoit aucune réalisation par défaut.
Une classe qui contient au moins une fonction virtuelle pure est une
classe abstraite.
class ABSTRAITE //Notion d’archetype
{
...
public:
virtual void info(..)=0;//virtuelle pure
};
Il n’est pas possible d’instancier un objet de type ABSTRAITE.
Il faut écrire des classes dérivées de ABSTRAITE.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 23/30
Héritage multiple/ambiguités (1)
Toutes les ambiguités d’accès doivent être levées à la compilation:
class B_1 class B_2 class D: int main()
{... {... public B_1, {
public: public: public B_2 D vd;
int i,ii; int* ii; { vd.i ..//OK de B_1
... ... ... vd.B_1::ii//de B_1
}; }; }; vd.B_2::ii//de B_2
}
Encore une fois, :: précise le référentiel.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 24/30
7. Héritage multiple/duplication (1)
Le graphe d’héritage peut amener la duplication d’une classe de
base:
liste_chaînée
liste_de_A liste_de_B
dérivé_A_B
matrice_carree
matrice_symétrique_définie_positive
matrice_symétrique matrice_définie_positive
Duplication souhaitable Duplication non souhaitable
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 25/30
Héritage multiple/duplication (2)
Si on ne fait rien ==> duplication
class GP class D1 class U: int main()
{... :public GP public D1, {
public: {... public D2 U u1;
int i; }; { //GP via D1
... ... u1.D1::i ...
}; class D2 }; //GP via D2
public GP u1.D2::i ...
{... }
}; }
L’utilisation de l’opérateur :: est obligatoire pour préciser le
référentiel.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 26/30
Héritage multiple/duplication (3)
Pour éviter la duplication: notion de “classe virtuelle”.
class GP class D1: class U: int main()
{... public virtual GP public D1, {
public: {... public D2 U u1;
int i; }; { u1.i ..
... class D2: ... //un seul i
}; public virtual G }; //(de GP)
{... };
};
Problème: l’héritage virtual n’est ni une propriété de GP, ni une
spécification de U.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 27/30
Récapitulation-2 (1)
Notions à bien comprendre:
Héritage simple, multiple.
privé/public/protégé.
Fonctions virtuelles.
Classes abstraites.
Classes virtuelles.
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 28/30
8. C++/Conclusions (1)
La généricité est utile pour des logiciels de grande taille et dans
l’optique réutilisation:
les types paramètres (template);
l’héritage simple;
l’héritage multiple (difficile à gérer).
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 29/30
C++/Conclusions (2)
Le langage C++
offre beaucoup de possibilités.
mais. . . il est aussi très “permissif”.
Il est indispensable d’avoir une méthodologie:
faire le “tri” (quelle caractéristique est utile à un moment
donné).
suivre une démarche de réalisation (les types, leurs
relations mutuelles).
GIP RENATER Philippe d’Anfray Objets en C++ 2006-2007 – p. 30/30