0% ont trouvé ce document utile (0 vote)
63 vues53 pages

L'Héritage en C++: POO - ENSI - 2011/2012

Transféré par

Amal Bouaziz
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
63 vues53 pages

L'Héritage en C++: POO - ENSI - 2011/2012

Transféré par

Amal Bouaziz
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

L’HÉRITAGE EN C++

Olfa FAKHFAKH POO – ENSI – 2011/2012


Plan
2

1. Concept & Principe


2. Visibilité entre héritier
3. Redéfinition des méthodes
4. Constructeurs & destructeurs
5. Type d’héritage
6. Compatibilité entre classe de base et classe dérivée
7. Méthodes non héritées
8. Héritage multiple
1. Concept & Principe
3

 Étendre un type abstrait :


maj. des services et/ou des comportements
 Mécanisme permettant de :
 ajouter de nouvelles fonctionnalités à une classe existante
 changer un peu le comportement de certaines méthodes d’une classe déjà
existante
On veut faire cela sans rien changer à la classe déjà existante

 On définira donc une nouvelle classe qui héritera de la classe existante


 En C++, on parlera de classe de base et de classe dérivée
(suite)
4

 Une classe dérivée hérite les attributs et les méthodes


de la classe de base (à l’exception de quelques unes)

 Mais, une classe dérivée peut redéfinir une méthode.


Ainsi, c’est cette méthode redéfinie qui sera appelée
pour un objet de cette classe, et non pas la méthode
originale de la classe supérieure
Exemple :
5

 Voyons d’abord la classe Employé:


class Employe {
public:
Employe(string nom_employe, double salaire_initial);
void set_salaire(double nouveau_salaire);
double get_salaire() const;
string get_nom() const;
private:
string nom;
double salaire;
};
Exemple : classe dérivée
6
héritage

class Directeur : public Employe {


public:
Directeur(string nom_employe, double salaire_initial);
void ajouter_employe(Employe* employe);
Employe get_employe(string nom) const;
private:
/* Collection d’employés */ employes_supervises;
};

Attribut spécifique à
Nouvelles méthodes
l’héritier
Instanciation d’une classe dérivée
7

Employe P1("Ali",100);
P1
Employe P2("Hédi",120);
Ali
Employe P3("Amira",110);
100
Directeur D("Salah",130);
D.ajouter_employe( &P1);
D.ajouter_employe( &P2);
D P2 P3
Salah Hédi
120 Amira
130
110

Représentation de l’héritage (UML)
8

Employe
0..* - nom
- salaire

Directeur

supervise
Remarques
9

 L’héritage est une relation de type « est un »


 Pour la classe Directeur qui est une classe dérivée de la classe Employe.
Tout directeur est aussi un employé, ce qui est tout à fait conforme à
l’intuition
 Il ne faut pas confondre l’héritage avec l’agrégation (ou la
composition)
 Il pourrait être attirant d’utiliser l’agrégation(ou la composition) au
lieu de l’héritage
 Du point de vue technique, le résultat peut paraître équivalent. Mais
il s’agit de deux concepts tout à fait différents
Exemple 2: un point coloré est un point
10

class point
{ /* déclaration des membres
privés */ class pointcol : public
int x ; point
int y ; {
/* déclaration des membres short couleur ;
publics */
public :
public :
void colore (short cl)
void initialise (int, int) ;
{ couleur = cl ; }
void deplace (int, int) ;
} ;
void affiche () ;
} ;
Exemple 2: un point coloré est un point
11

Chaque objet de type pointcol peut


main()
alors faire appel :
{ pointcol p ; • aux méthodes publiques de pointcol
p.initialise (10,20) ; (ici colore),
p.colore (5) ; • aux méthodes publiques de la classe
p.affiche () ; de base point (ici initialise, deplace et
affiche).
p.deplace (2,4) ;
p.affiche () ;
}

Je suis en 10 20
Je suis en 12 24
2. Visibilité entre héritier
12

• Lors de l’héritage public, les attributs privés de la classe de


base deviennent inaccessibles pour les classes dérivées.

 Il faut donc utiliser les méthodes publiques de la classe de


base afin d’accéder à ces attributs

• Les attributs protégés de la classe de base seront accessibles


par les classes dérivées, mais ne seront pas accessibles par les
utilisateurs de la classe dérivée.
Utilisation des membres de la classe de base
dans une classe dérivée
13

class pointcol : public point


void pointcol::affichec ()
{…
{affiche () ;
void affichec ()
cout << " et ma couleur est : " <<
{ cout << "Je suis en " << x << " "
couleur << "\n" ;
<< y << "\n" ;
}
cout << " et ma couleur est : " <<
couleur << "\n" ;}
}; void pointcol::initialisec (int abs, int
ord, short cl)
{ initialise (abs, ord) ;
Une méthode d’une classe dérivée
n’a pas accès aux membres privés couleur = cl ;
de sa classe de base. }
Les membres protégés
14

Les membres protégés sont :

 inaccessibles à l'utilisateur de la classe (comme des membres


privés).
 accessibles aux membres d'une éventuelle classe dérivée,
 inaccessibles aux utilisateurs de cette classe dérivée
Les membres protégés
15 class pointcol : public point
{
class point
short couleur ;
{ protected :
public :
int x, y ;
void affiche ()
public : { cout << "Je suis en " << x << " " <<
point ( ... ) ; y << "\n" ;
affiche () ; cout << " et ma couleur est " << couleur
<< "\n" ;
.....
}
}; Main(){ ….
point p; };
p.x=0;
p.y=0;

}
Exemple : visibilité
16

class A {
private : class B : public A {
int i; public :
public : void f()
int j; {
protected : void main() i = 0;
int k; { j = 0;
}; A a; k = 0;
a.i = 0; }
a.j = 0; };
a.k = 0;
}
Exemple : Horloge
17

 Soit la classe Horloge, qui permet d’obtenir


l’heure locale, de deux façons:

 normale : (23:45)
 à l’américaine [am/pm] (pm 11:45)
Cas : Horloge sans héritage
18

void Horloge::afficher()
class Horloge {
{
CC hh,mm;
if ( a_americaine)
bool a_americaine;
// am hh:mm ou pm hh:mm
public :
else
Horloge(int h, int m
// hh:mm
bool americaine = false)
}
:hh(h,24),mm(m,60)
{a_americaine = amercicaine;}
void operator ++(); void main()
int valh(); {
int valm(); Horloge H1(22,50,true);
void afficher(); Horloge H2(23,55,false);
}; }
Cas : Horloge avec héritage
19

class Horloge {
Horloge CC hh,mm;
public :
Horloge(int h, int m);
int valh();
Clock
int valm();
void afficher();
class Clock : public Horloge };
{
void main()
public :
{
Clock(int h, int m);
Horloge H1(22,50);
...
Clock H2(23,55);
};
}
3. Redéfinition des méthodes de base
20

• Les méthodes de la classe de base peuvent être


redéfinies dans la classe dérivée.

• Les méthodes redéfinies de la classe de base


demeurent accessibles via l'opérateur de résolution
de portée ("::").
Cas : Clock – classe dérivée
21

void Clock::afficher()
class Clock : public Horloge
{
{
if ( valh() <= 12 )
public :
// am h:m
Clock(int h, int m);
else
void afficher ();
// pm h-12:m
};
}

void main()
{
Clock H1(22,50);
H1.afficher();
H1.Horloge::afficher();
}
Exemple :
Redéfinition de méthodes
22

void Horloge::afficher()
{ cout<< valh() << ":" << valm() << endl; }

void Clock::afficher()
{
if ( valh() < 12 ) {
cout << "am ";
Appel de la fonction
Horloge::afficher();
afficher() du parent
} else {
cout << "pm " << valh() -12 << ":" << valm() << endl;
}
Redéfinition des fonctions membres d’une
classe dérivée void pointcol::affiche ()
{
23
point::affiche () ; // appel de affiche de la classe point
cout << couleur << "\n" ;
class pointcol : public point{
}
public :
void colore (short cl) void pointcol::initialise (int abs, int ord, short cl)
{ couleur = cl ; } {
void affiche () ; point::initialise (abs, ord) ;
void initialise (int, int, short); couleur = cl ;
}; }

Il faut alors faire appel à l'opérateur


de résolution de portée (::) pour
localiser convenablement la méthode
void main(){
pointcol p ; voulue
p.initialise (10,20, 5) ; p.affiche () ;
p.point::affiche () ; // pour forcer l'appel de affiche de point
p.deplace (2,4) ; p.affiche () ;
p.colore (2) ; p.affiche () ;
}
Redéfinition des membres données d’une
classe dérivée
24

class A
{ .....
class B : public A NB. le membre a défini dans B
int a ; s'ajoute au membre a hérité de A ;
{ float a ;
char c ; il ne le remplace pas.
.....
.....
};
};

main()
{ B b;
b.a; // fait référence au membre a de type float de b.
b.A::a; // fait référence au membre a de type int de a.
};
Redéfinition et surdéfinition
25
class A lorsqu’une fonction est redéfinie
{ public : dans une classe dérivée, elle
class B : public A masque une fonction de même
void f(int n) { ..... }
signature de la classe de base.
void f(char c) { ..... } { public :
// f est surdéfinie dans A void f(float x) { ..... }
// on ajoute une troisème
}; définition dans B
};
main()
{ int n ; char c ; A a ; B b ;
a.f(n) ; // appelle A:f(int) (règles habituelles)
a.f(c) ; // appelle A:f(char) (règles habituelles)
b.f(n) ; // appelle B:f(float) (alors que peut-être A:f(int) conviendrait)
b.f(c) ; // appelle B:f(float) (alors que peut-être A:f(char) conviendrait)
}
Redéfinition et surdéfinition class B : public A
{ public :
class A
26
void f(float x) { ..... }
{ public :
void f(int x) { ..... }
void f(int n) { ....}
void f(char c) { ...} } ;

void g(int n) { ....}


main()
void g(char c) { ....} { int n ; char c float x; A a ; B b ;
} ; a.f(n) ; // appelle A:f(int) (règles habituelles)
a.f(c) ; // appelle A:f(char) (règles habituelles)

b.f(n) ; // appelle B:f(int)


b.f(x) ; // appelle B:f(float)
b.f(c) ; // appelle B:f(int)après conversion de c en int et no
pas A:f(char) (alors que peut-être A:f(char) conviendrait)

b.g(n) ; // appelle A:g(int)


b.g(x) ; // appelle A:g(int) ou A:g(char)
b.g(c) ; // appelle A:g(char)
Redéfinition et surdéfinition
27
class A
{ public :
void f(int n) { ..... } class B : public A Une redéfinition d’une méthode
void f(char c) { ..... } { public : dans une classe dérivée cache
en quelque sorte les autres.
}; void f(int, int) { ..... }
};

main()
{ int n ; char c ; B b ;
b.f(n) ; // erreur de compilation
b.f(c) ; // erreur de compilation
}
4. Constructeur et destructeur
28

Lors de la création d’un objet d’une classe dérivée, les


constructeurs sont appelés dans l’ordre suivant:
1) Les constructeurs des objets attributs de la classe de
base,
2) Le constructeur de la classe de base,
3) Les constructeurs des objets attributs de la classe
dérivée;
4) Le constructeur de la classe dérivée.
Les destructeurs sont appelés en ordre inverse des
constructeurs.
Exemple : constructeur
29

void main()
class A {
{
public :
B b;
A() { cout << "A::A()" << endl;}
cout << "**" << endl;
~A() { cout << "A::~A()" << endl;}
}
};

class B : public A { A::A()


public : B::B()
B() { cout << "B::B()" << endl;} **
~B() { cout << "B::~B()" << endl;} B::~B()
}; A::~A()
Transmission d'informations entre
30
constructeurs
class point
{… class pointcol : public point
public : {…
point (int , int ); public :
… pointcol (int, int, short) ;
~pointcol ();
~point ();

… };
}; …
pointcol::pointcol (int abs, int ord, short cl) :
point (abs, ord)
{
Couleur = cl;
}
Constructeur de Horloge & Clock
31

Horloge::Horloge(int h, int m)
: hh(h), mm(m)
{

Clock::Clock(int h, int s) : Horloge(h,s)


{

}
5. Type d’héritage
32

 Il existe trois différents types d’héritage, soient :


 public, private et protected.

 Le type d’héritage est spécifié après le symbole :


 class point: public pointcol

 Par défaut, le type d’héritage est privé.


 class point : pointcol  class point: private pointcol

 Le Type d’héritage conditionne


la visibilité des héritiers
Héritage public
class ClasseDerivee : public ClasseBase

Classe de Classe
base dérivée
private  Inaccessible
protected  protected
public  public

Les attributs private, protected et public de la classe de base


restent les mêmes pour la classe dérivée.

33
Héritage protected
class ClasseDerivee : protected ClasseBase

Classe de Classe
base dérivée
private  Inaccessible
protected  protected
public  protected

Les attributs public de la classe de base deviennent protégés pour


la classe dérivée.

34
Héritage privé
class ClasseDerivee : private ClasseBase

Classe de Classe
base dérivée
private  Inaccessible
protected  private
public  private

Tous les attributs de la classe de base deviennent private pour la


classe dérivée.

35
Héritage privé: exemple
36
class pointcol : private point{
class point { public :
public : pointcol (...) ;
Void point(); void colore (...) ;
};
void affiche () ;
void deplace (...) ;
};
void main()
{
pointcol p ;

p.affiche ();
p.deplace (...);

p.colore (...);

}
Droits d'accès sur les membres hérités

mot clé utilisé pour l'héritage

Accès aux données public protected private

public public protected private


mot clé utilisé
pour les protected protected protected private
champs et les
méthodes
private private private private

37
6. Compatibilité entre classe de base
et classe dérivée
38

 Cette compatibilité entre une classe dérivée et sa classe de


base ne s'applique que dans le cas de dérivation publique.
 Elle se résume à l'existence de conversions implicites :
 d'un objet d'un type dérivé dans un objet d'un type de base,
 d'un pointeur (ou d'une référence) sur une classe dérivée en un
pointeur (ou une référence) sur une classe de base.
 On traduit souvent ces propriétés en disant que l’héritage
réalise une relation « est un» entre la classe dérivée et la
classe de base :
 tout objet de type pointcol est un point, mais tout objet de
type point n’est pas un pointcol.
Conversion d'un type dérivé en un type de base
39

 Entraîne une conversion de b dans le type


point a ; point et l'affectation du résultat à a.
pointcol b ;  Cette affectation se fait, suivant les cas :
a=b; • par appel de l'opérateur d'affectation
(de la classe point) si celui-ci a été
surdéfini,
• par emploi de l'affectation par défaut
dans le cas contraire

point a ;
pointcol b ;
 l'affectation suivante serait rejetée
b=a ;
Conversion de pointeurs
40

class point
{ int x, y;
class pointcol : public point
public :
{ short couleur ;
.....
public :
void affiche () ;
..... point * adp ;
};
void affiche () ; pointcol * adpc ;
};
adp = adpc ;
une conversion du type pointcol *
dans le type point *. adpc = adp ;

adpc = (pointcol *) adp ;


7. Méthodes non héritées
41

Les classes dérivées n’héritent pas :


• Des constructeurs
(défaut, paramètres, copie);
• Du destructeur;
• De l’opérateur d’affectation;
• Des relations d’amitié.
Le constructeur de recopie et l’héritage
42

1. Si la classe dérivée ne définit pas de constructeur de recopie,


il y aura appel du constructeur par recopie par défaut de la
classe dérivée, lequel procédera ainsi :

 appel du constructeur par recopie de la classe de base (soit


celui qui y a été défini, soit le constructeur par recopie par
défaut) ;
 initialisation des membres donnée de la classe dérivée qui ne
sont pas hérités de la classe de base.
Le constructeur de recopie et l’héritage
43

2. Si la classe dérivée définit explicitement un constructeur par


recopie :
 Le constructeur de recopie de la classe dérivée doit prendre en
charge l’intégralité de la recopie de l’objet, et non seulement de
sa partie héritée.
 Il faut tenir compte de ce que l’appel de ce constructeur par
recopie entraînera l’appel du constructeur de la classe de base
mentionné dans son en-tête

class A { ... } ;
class B : public A { ... } ;
 Appel du constructeur par recopie de A
auquel sera transmise la partie de B héritée de A
B (B & b) : A(b) ; (grâce aux règles de compatibilité entre
classe dérivée et classe de base)
Exemple de constructeur de recopie:
44

class point
class pointcol : public point
{ int x, y ;
{ char coul ;
public :
public :
point (int abs=0, int ord=0)
pointcol (int abs=0, int ord=0, int cl=1)
{ x = abs ; y = ord ;} : point (abs, ord)
point (point & p) { coul = cl ; }
{ x = p.x ; y = p.y ;} pointcol (pointcol & p) : point (p)
}; { coul = p.coul ;}
};

il y aura conversion implicite


de p dans le type point
L’opérateur d’affectation et l’héritage
45

1.
• Si on a un opérateur= dans la classe de base;
• Si aucun opérateur= dans la classe dérivée;

Alors, le compilateur va appeler l’opérateur= de la classe de


base et recopie attribut par attribut les attributs de la classe
dérivée.
L’opérateur d’affectation et l’héritage
46

2. Si la classe dérivée surdéfinit l'opérateur =


 L'affectation de deux objets de type B fera alors
nécessairement appel à l'opérateur = défini dans B.
 Celui de A ne sera pas appelé, même s'il a été surdéfini.

 Il faudra donc que l'opérateur = de B prenne en charge tout


ce qui concerne l'affectation d'objets de type B, y compris pour
ce qui est des membres hérités de A
Exemple de l’opérateur d’affectation:
class point
47 class pointcol : public point
{ int x, y ;
{ int coul ;
public :
public :
point (int abs=0, int ord=0)
pointcol (int abs=0, int ord=0, int cl=1) :
{ x = abs ; y = ord ;} point (abs, ord)
point & operator = (point & a) { coul = cl ; }
{ x = a.x ; y = a.y ; pointcol & operator = (pointcol & b)
return * this ;} { coul = b.coul ;
}; return * this ;}
main()
};
{ pointcol p(1, 3, 10) , q(4, 9, 20) ;
q=p;
l'opérateur = défini dans la classe point
q.affiche () ;
n'a pas été appelé lors d'une affectation
} q avant = pointcol : 4 9 20
entre objets de type pointcol.
operateur = de pointcol
q apres = pointcol : 4 9 10
L’opérateur d’affectation et l’héritage
48

 Comment forcer, dans une classe dérivée, l'utilisation de


l'opérateur = surdéfini dans la classe de base :

class pointcol : public point


{….
pointcol & operator = (pointcol & b)
{point * ad1, * ad2 ;
ad1 = this ; // conversion pointeur sur pointcol en pointeur sur point
ad2 = & b ; // idem
* ad1 = * ad2 ; // affectation de la "partie point" de b
coul = b.coul ; // affectation de la partie propre à pointcol
return * this ;
};
8. Héritage multiple
49

 Une classe héritière possède plusieurs classes parentes


class B :
class A :
{public :
{public :
int g() {return 2*i;}
int f() {return i;}
private :
private :
int j;
int i;
};
};
class C : public A, public B
{
public :
int h();
}
Pb1 :
Que se passe-t-il si un même identifiant est utilisé dans deux classes parentes ?

50

class B :
class A :
{public :
{public :
int f() {return 2*i;}
int f() {return i;}
private :
private :
int i;
int i;
};
};
class C : public A, public B
{
public :
int g();
}; Redéfinir f() dans C
Exemple : pointcoul hérite de point et de coul
51 class point Class coul
{int x, int y; { short couleur;
public :
public :
coul (int );
point (int , int );
~coul();
~point ();
Affiche();
Affiche(): };
}; Class pointcoul : public point, public coul
{…
public :
public :
pointcoul::pointcoul (int abs, int ord, int cl) : point (abs,
ord), coul (cl) { … }
~pointcoul ();
void affiche ()
{ point::affiche () ; coul::affiche () ;}
};
Pb2 :
Héritage répété : un hydravion hérite des classes d’avion et bateau qui toute deux héritent de véhicule.
Que deviennent les caractéristiques de la classe répétée ? Sont-elles dupliquées ou fusionnées ?
52

class A {
public :
int f() {return i;}
};
class B : public A { class C : public A {
}; };

class D : public B, public C {


public :
int g(); Héritage virtuel :
}; class B : virtual public A
Conclusion
53

• Les classes dérivées sont un mécanisme simple pour définir une


nouvelle classe en ajoutant des facilités à une classe existante
sans reprogrammer ou recompiler la classe de base.

• En utilisant les classes dérivées d’une classe existante, on définit


une interface commune aux classes dérivées de telle manière
que les objets de ces classes dérivées sont manipulés de façon
identique par certaines parties du programme.
Réutilisation
• On peut ainsi utiliser l’héritage pour les besoins de
généralisation, de réutilisation.

Vous aimerez peut-être aussi