Sunteți pe pagina 1din 23

LHERITAGE EN C++

LHERITAGE EN C++

Plan du chapitre :

I. Introduction .
.. 64
II. Hritage simple .
66
III. Hritage multiple
...
... 69
IV. Hritage et constructeur......
constructeur...... 70
V. Compatibilit entre classe de base et classe drive ..... 72
VI. Le constructeur
ucteur de recopie et lhritage .... 74
VII. Le polymorphisme ...... 76
VIII.Lhritage et la surcharge des oprateurs .
... 83

Objectifs du chapitre :
 Comprendre le mcanisme dhritage en programmation oriente objets et lappliquer en C++.
 Dcouvrir la technique de polymorphisme en langage C++.
C++

BEN ROMDHAN Mourad 63


LHERITAGE EN C++

I. Introduction:
I.1 Exemple :
Imaginons que nous devions fabriquer un logiciel qui permet de grer une bibliothque. Cette
bibliothque comporte plusieurs types de documents ; des livres, des CDs, ou des DVDs. Une
premire tude nous amne mettre en uvre les classes suivantes :

Nous remarquons que dans les trois types de documents, un certain nombre de caractristiques
se retrouvent systmatiquement.
Afin d'viter la rptition des lments constituant chacune des classes, il est prfrable de
factoriser toutes ces caractristiques communes pour en faire une nouvelle classe plus gnraliste.
En effet, nous pouvons dire que, d'une faon gnrale, et quelque soit le type de document, il
comporte au moins un titre, un auteur, etc. il semble allait de soi, que le nom de cette nouvelle
classe gnrale s'appelle justement Document.

La gnralisation se reprsente par une flche qui part de la classe fille vers la classe mre. Par
exemple, Un Livre possde, certes un nombre de page, mais en suivant la flche indique par la
relation de gnralisation, elle comporte galement un nom d'auteur, un titre, une rfrence. En
fait, la classe Livre hrite de tout ce que possde la classe Document, les attributs comme les
mthodes.

BEN ROMDHAN Mourad 64


LHERITAGE EN C++

Dans cet exemple, nous avons un seul niveau d'hritage, mais il est bien entendu possible d'avoir
une hirarchie beaucoup plus dveloppe. D'ailleurs, si nous regardons de plus prt, nous
remarquons que nous pouvons appliquer une nouvelle fois la gnralisation en factorisant la dure
du support CD et du support DVD. En fait, il s'agit dans les deux cas d'un support commun appel
Multimdia.

I.2 Dfinitions :
 Lhritage est une technique permettant de construire une classe partir dune ou des plusieurs
autres classes dites : classe mre ou superclasse ou classe de base.
 La classe drive est appele: Classe fille ou sous-classe.

Relation is a (est un)

 Les sous-classes hritent des caractristiques de leurs classes parents.


 Les attributs et les mthodes dclares dans la classe mre sont accessibles dans les classes fils
comme sils avaient t dclars localement.

 Crer facilement de nouvelles classes partir de classes existantes (rutilisation du code)

BEN ROMDHAN Mourad 65


LHERITAGE EN C++

II. Hritage simple:


La classe drive hrite les attributs et les mthodes dune seule classe mre.

II.1 Syntaxe :
class C_mere
{
//...
};
class C_derivee: <mode> C_mere
{
//...
};

mode: optionnel permet dindiquer la nature de lhritage : private, protected ou public.


 Si aucun mode nest indiqu alors lhritage est priv.

II.2 Exemple :

Point Classe de base

Point couleur
Classe drive

class Point
{
int x;
int y;
public:
void initialise (int , int) ;
void afficher() ;
};
class Pointcol: public Point
{
int couleur;
public:
void setcol(int c)
{
couleur=c ;
}
};
void main()
{
Pointcol p ;
p.initialiser (10,20) ;
p.setcol(5) ;
p.afficher() ;// (10, 20)
}

BEN ROMDHAN Mourad 66


LHERITAGE EN C++

II.3 Utilisation des membres de la classe de base dans une classe drive :
Lexemple prcdent, destin montrer comment sexprime lhritage en C++, ne cherchait pas
explorer toutes les possibilits.
Or la classe pointcol telle que nous lavons dfinie prsente des lacunes. Par exemple, lorsque
nous appelons affiche pour un objet de type pointcol nous nobtenons aucune information sur sa
couleur.
Une premire faon damliorer cette situation consiste crire une nouvelle fonction membre
public :
void afficherC()
{
cout << "("<<x<<","<<y<<")"<<endl ;
cout << "couleur="<< couleur<<endl ;
}
Mais alors cela signifierait que la fonction afficherC membre de la classe Pointcol aurait un accs aux
membres privs de point ce qui serait contraire au principe dencapsulation.
En revanche, rien nempche une classe drive daccder nimporte quel membre public de sa
classe de base. Dou une dfinition possible de afficherC :
void afficherC()
{
afficher() ;
cout << "couleur="<< couleur<<endl ;
}
Dune manire analogue, nous pouvons dfinir dans pointcol une fonction dinitialisation comme
initialiserC :
void initialiserC(int a, int b, int c)
{
initialiser(a,b) ;
couleur=c ;
}

II.4 Contrle daccs pendant l'hritage:


Statut des membres de la classe drive en fonction du statut des membres de la classe de base et du mode
de drivation :
Statut des membres de base
Public Protected Private
Public Public Protected Inaccessible
Mode de
drivation Protected Protected Protected Inaccessible
Private Private Private Inaccessible
Remarque :
 Lorsquune classe drive possde des fonctions amies, ces derniers disposent exactement des
mmes autorisations daccs que les fonctions membres de la classe drive. En particulier, les
fonctions amies dune classe drive auront bien accs aux membres dclars protgs dans sa
classe de base.
 En revanche, les dclarations damiti ne shritent pas. Ainsi, si f a t dclar amie dune
classe A et si B drive de A, f nest pas automatiquement amie de B (il est bien sur possible de
prvoir une dclaration damiti approprie dans B).
 Java permet dinterdire une classe de donner naissance des classe drives (en la dclarant
avec le mot cl final dans la classe de base).

BEN ROMDHAN Mourad 67


LHERITAGE EN C++

 Drivation publique:

1. Les membres publics de la classe de base


sont accessibles tout le monde , c'est--
dire la fois aux mthodes, aux fonctions
amies de la classe drive ainsi qu'aux
utilisateurs de la classe drive.
2. Les membres protgs de la classe de base
sont accessibles aux mthodes et aux
fonctions amies de la classe drive, mais
pas aux utilisateurs de cette classe drive.
3. Les membres privs de la classe de base
sont inaccessibles la fois aux mthodes ou
aux fonctions amies de la classe drive et
aux utilisateurs de la classe drive.

 Drivation publique:

- Les membres hrits publics et protgs


d'une classe de base prive deviennent des
membres privs de la classe drive.
- Cette technique permet d'interdire, aux
utilisateurs d'une classe drive, l'accs aux
membres publics de sa classe de base. Cela
sous-entend que seules les mthodes de la
classe drive devront tre utilises, sinon il
faudra redfinir les mthodes de la classe de
base (voir plus loin). Pour les drivations
partir de la classe drive, l'accs la classe
de base devient alors totalement
inaccessible, les petits enfants n'ont donc
pas accs aux membres de leur grands-
parents.

 Drivation protge:

- Les membres hrits publics et protgs


d'une classe de base protge deviennent
des membres protgs de la classe drive.
Nous retrouvons le mme principe que pour
une drivation prive, la seule diffrence
concerne les enfants ventuels de la classe
drive, puisque dans ce cas l, les petits
enfants peuvent atteindre des membres
protgs.

BEN ROMDHAN Mourad 68


LHERITAGE EN C++

III. Hritage multiple:


La classe drive hrite les attributs et les mthodes partir de plusieurs classes mres.

Exemple :

Remarque :

Lhritage multiple est possible en C++ mais permet de poser quelques problmes :

 Si les deux classes mres ont des attributs ou des mthodes de mme nom (collision des noms
lors de la propagation).
 La classe D hrite deux fois les attributs de A, une fois travers la classe B et lautre fois
travers C.

 Java ne supporte pas lhritage multiple.


Syntaxe :

class C_mere1
{
//...
};
class C_mere2
{
//...
};
class C_derivee: <mode> C_mere1, <mode> C_mere2
{
//...
};

BEN ROMDHAN Mourad 69


LHERITAGE EN C++

IV. Hritage et constructeur:


Si la classe mre contient un constructeur dont lappel est obligatoire alors le constructeur de la
classe drive doit obligatoirement appel celui de la classe mre.
Il faut spcifier le nom et les paramtres du constructeur mre aprs le prototype du constructeur
fils.
 Exemple:
class Point
{
int x,y;
public:
Point(int, int) ;
};
class Pointcol: public Point
{
int couleur;
public:
Pointcol(int a, int b, int c): Point(a, b)
{
couleur=c;
}
};
Il est possible de mentionner des arguments par dfaut dans Pointcol, par exemple :
Pointcol(int a=0, int b=0, int c=1): Point(a, b)
Dans ces conditions, la dclaration :
Pointcol b(5) ;
Entrainera :
 lappel de Point avec les arguments 5 et 0
 lappel de Pointcol avec les arguments 5, 0 et 1

 Hirarchisation des appels:

Soit les classes suivantes:

class A class B : public A


{ ...... { ......

public: public:
A(...) ; B(...) ;
~A() ; ~B() ;
.... ....
}; };

Pour crer un objet de type B, il faut tout dabord crer un objet de type A, donc faire appel au
constructeur de A, puis le complter par ce qui est spcifique B et faire appel au constructeur de
B. ce mcanisme est pris en charge par C++ : il ny aura pas prvoir dans le constructeur de B
lappel du constructeur de A.
La mme application sapplique aux destructeur : lors de la destruction dun objet de type B, il y
aura automatiquement appel du destructeur de B, puis appel de celui de A(les destructeurs sont
appels dans lordre inverse de lappel des destructeurs).

BEN ROMDHAN Mourad 70


LHERITAGE EN C++

 Exemple:
class Point
{
int x;
int y;
public:
Point(int abs=0, int ord=0)
{
cout <<"++constr. point: "<<abs<<, <<ord<<endl ;
x=abs ; y=ord ;
}
~Point()
{
cout <<"desctr. point: "<<x<<","<<y<<endl ;
}
};
class Pointcol: public Point
{
int couleur;
public:
Pointcol(int , int , int) ;
~Pointcol ()
{
cout<<"destr. pointcol -couleur: "<<couleur<<endl ;
}
};
Pointcol::Pointcol(int abs=0,int ord=0,int c=1):Point(abs,ord)
{
couleur=c;
cout <<"++constr. pointcol: "<<abs<<, <<ord<<","<<couleur;
}
void main()
{
Pointcol a(10,15,3) ;
Pointcol b(2,3) ;
Pointcol c(12) ;
Pointcol *adr;
adr = new Pointcol(12,25);
delete adr ;
}

BEN ROMDHAN Mourad 71


LHERITAGE EN C++

Remarque :

Quelque soit les situations, nous disposons toujours des quatre mmes phases pour la cration de
l'objet et toujours dans le mme ordre.
1. Allocation mmoire ncessaire pour contenir tous les attributs que comporte l'objet.
2. Appel du constructeur de la classe drive. Ce constructeur est appel mais pas encore
excut. Appel du constructeur de la classe de base spcifi par la liste d'initialisation en
rcuprant les bons arguments pour les attributs de la classe de base.
3. Appel et excution du constructeur de la classe de base. A moins que la classe de base soit
elle-mme une classe drive d'une autre classe de base, les instructions qui constituent le
corps du constructeur sont excutes.
4. Excution du constructeur de la classe drive. Puisque la partie gnrale est bien initialise,
nous pouvons nous occuper de la partie spcifique la classe drive. Les instructions du
corps du constructeur sont donc excutes.

V. Compatibilit entre classe de base et classe drive :


Nous pouvons toujours dire qu'un cercle est aussi une forme et qu'un lve est aussi une personne.
La rciproque n'est pas vraie. Selon le besoin, nous savons tablir des conversions implicites qui
permettent de passer d'un type vers un autre. Dans le cadre de l'hritage, il sera possible de passer
d'une classe drive vers une classe de base. Par contre l'inverse ne sera pas possible.

 Exemple:
class Forme
{
int x;
int y;
public:
Forme(int x, int y)
{ this->x=x ; this->y=y ;}
void deplacer(int dx, int dy)
{ x+=dx;y+=dy ; }
};
class Cercle: public Forme
{
int rayon;
public:
Cercle(int x, int y, int r):Forme(x, y)
{ rayon=r ; }
void agrandir(int a)
{ rayon+=a; }
};
void main()
{
Forme f1(2,3); //1//
Cercle c1(15, -1, 50) ; //2//
c1.deplacer(3, 4) ; //3//
c1.agrandir(10) ; //4//
f1=c1 ; //5//
f1.deplacer(5, -8) ; //6//
c1=f1 ; //7//
}

BEN ROMDHAN Mourad 72


LHERITAGE EN C++

1. Cration de l'objet f1.


2. Cration de l'objet c1.
3. Possibilit de dplacer le cercle c1 puisque la mthode a t hrite.
4. Agrandissement du cercle c1 grce la mthode agrandir dfinie par la classe Cercle.

5. droite et gauche de l'oprateur d'affectation, les types sont diffrents. C'est toujours le
type qui est droite qui est transform vers le type de gauche. Ici, le cercle c1 est aussi une
forme, donc la conversion implicite est lance. Cette dmarche parat normale puisqu' l'issue
de cette opration, les attributs de l'objet f1 sont parfaitement dfinis.

6. Dans ce contexte, il est galement possible de changer de position puisque, de toute faon, la
mthode associe a t dfinie dans la classe Forme.

7. Tentative daffectation d'une forme dans un cercle. Nous obtenons galement une erreur de
compilation. Il s'agit galement d'un changement de type. Si ce casting tait tolr, cela
voudrait dire que nous autoriserions d'avoir des attributs avec des valeurs alatoires !.
Effectivement f1 ne dispose pas de rayon, ainsi l'attribut rayon de c2 se retrouverait sans
aucune valeur bien prcise. Cette dmarche n'est pas tolre par le compilateur et nous le
comprenons.

!
BEN ROMDHAN Mourad 73
LHERITAGE EN C++

VI. Le constructeur de recopie et lhritage:


Nous savons que le constructeur de recopie est appel en cas :

 dinitialisation dun objet par un objet de mme type,


 de transmission de la valeur dun objet en argument ou en retour dune fonction.
Un constructeur de classe de base est toujours invoqu avant l'excution du constructeur de la
classe drive. Le constructeur de copie est galement un constructeur. Il ne fait donc pas
exception cette rgle, ce qui est logique. Nous retrouvons donc les quatre mmes phases pour la
cration d'un objet par un autre.
Examinons les diverses situations possibles, en supposant que lon ait affaire aux instructions
suivantes (B drive de A) :

class A {...} ;
class B : public A {...};
void fct(B); //function recevant un argument de type B
...
B b1(...);
fct(b1);// appel de fct qui on doit transmettre b1
//par valeur ce qui implique lappel dun constructeur
//par de recopie de la classe B

Bien entendu, tout ce que nous allons dire sappliquerait galement aux autres situations
dinitialisation par recopie, c'est--dire o une fonction renverrait par valeur un rsultat de type B
ou encore celui o lon initialiserait un objet de type B avec un autre objet de type B comme
dans B b2=b1 ou encore B b2(b1).

VI.1 La classe drive na pas dfini de constructeur de recopie :


Il y a donc appel du constructeur de recopie par dfaut de B. Rappelons que la recopie se fait
membre par membre. Nous avons vu ce que cela signifiat dans le cas des objets membres.

Ici, cela signifie que la partie de b1 appartenant la classe A sera traite comme un membre de
type A. Il y aura donc appel du constructeur de recopie de A pour les membres donnes
correpondants. Notez bien que si A a defini un tel constructeur, il sera appel ; dans le cas
contraire, on se servira du constructeur de recopie par dfaut de A.

VI.1 La classe drive na pas dfini de constructeur de recopie :


Le constructeur de recopie de B est alors naturellement appel. Mais la question qui se pose est de
savoir sil y a appel dun constructeur de A. En fait, C++ a dcid de ne prvoir aucun appel
automatique de constructeur de la classe de base dans ce cas (mme sil existe un constructeur de
recopie dans A !). Cela signifie que le constructeur de recopie de la classe drive doit prendre en
charge lintgralit de la recopie de lobjet et non seulement sa partie hrite.

B(B &x): A(...)

En gnral, on souhaitera que le constructeur de A appel ce niveau soit le constructeur de


recopi de A. dans ces conditions, on voit que ce constructeur doit recevoir en argument nonpas
lobjet x tout entier, mais seulement ce qui, dans x, est de type A.
B(B &x): A(x) //

BEN ROMDHAN Mourad 74


LHERITAGE EN C++

 Exemple:

Voici un programme illustrant cette possibilit. Nous les deux classes point et pointcol en les
munisant toutes les deux dun constructeur de recopie:

class Point
{
int x;
int y;
public:
Point(int abs=0, int ord=0)
{
cout <<"constr. point: "<<abs<<, <<ord<<endl;
x=abs; y=ord ;
}
Point(Point &p)
{
x=p.x; y=p.y;
cout <<"constr. par recopie point:"<<x<<","<<y<<endl;
}
};
class Pointcol: public Point
{
int couleur;
public:
Pointcol(Pointcol &p):Point(p) // conversion implicite de p
{ // dans le type point
couleur=p.couleur;
cout <<"constr. par recopie pointcol:"<<couleur<<endl;
}
Pointcol(int abs=0,int ord=0,int c=1):Point(abs,ord)
{
couleur=c;
cout <<"++constr. pointcol:"<<abs<<","<<ord<<","<<couleur;
}
};
void fct(Pointcol p)
{
cout << " *** entree dans fct ***\n";
}
void main()
{
Pointcol a(2,3,4);
Pointcol b(a);
fct(a); //appel de fct, qui on transmet a par valeur
}

BEN ROMDHAN Mourad 75


LHERITAGE EN C++

VII. Le polymorphisme:
VII.1 Dfinition:
Mcanisme qui consiste dfinir des fonctions de mme nom dans les classes de bases et les
classes drives.
Les classes drives hritent tous les membres publics et protgs de la classe mre.

VII.2 Redfinition des membres donns dune classe drive:


Soit les classes suivantes:
class A
{
......
int a ;
char b ;
......
};
class B: public A
{
float a ;
......
};
Dans ce cas, si lobjet b est de type B, b.a fera rfrence au membre a de type float de b. Il sera
toujours possible daccder au membre donne a de type int (hrite de A) par b.A ::a.
Notez bien que le membre a dfini dans B sajoute au membre a hrit de A ; il ne le remplace pas.

VII.3 Redfinition des fonctions membres dune classe drive:


Dans lexemple de classe Pointcol, nous disposons la fois :
 dans point, dune fonction membre nomme afficher,
 dans pointcol, dune fonction membre nomme afficherC.
Or ces deux mthodes font le mme travail, or on pourrait souhaiter leur donner le mme nom.
Ceci est effectivement possible en C++.
 Remarque:

Il ne faut pas mlanger la redfinition et la surdfinition.


1. Une surdfinition (ou surcharge) permet d'utiliser plusieurs mthodes qui portent le mme
nom au sein d'une mme classe, avec une signature diffrente, pour que le systme puisse s'y
retrouver.

2. Une redfinition permet de fournir une nouvelle dfinition d'une mthode d'une classe
ascendante et ainsi de substituer la description qui en t faite. Nous avons galement le
mme nom que la mthode parente mais surtout avec une signature rigoureusement identique.
La redfinition constitue la base du polymorphisme.

Voici comment nous pouvons transformer lexemple du paragraphe prcdent en nommant affiche
et initialise les nouvelles fonctions membres de pointcol :

BEN ROMDHAN Mourad 76


LHERITAGE EN C++

#include "point.h"
class Pointcol: public Point
{
int couleur;
public:
void afficher()
{ Point::afficher() ;
cout << "couleur="<<couleur<<endl ;
}
void initialiser(int a, int b, int c)
{
Point::initialiser(a,b) ;
couleur=c ;
}
};

Remarquer que dans chaque classe les mthodes afficher() et initialiser() portent la mme
signature mais contiennent un code diffrent propre la spcificit de la classe.
Premire utilisation :

void main()
{
Pointcol *a= new Pointcol(2,3,4);
Point *b=new Point(a);
b->afficher();
}

Resultat de lexcution : (2,3)


 appel de la mthode afficher() de la classe mre.

Commentaire: Cest bien dutiliser la mme signature mais o est la notion du polymorphisme ?
Lhritage et le polymorphisme sont des mcanismes trs puissants mais jusque l les mthodes
ont une liaison statique la compilation.

Question : comment faut-il faire pour appliquer le corps spcifique chaque classe dynamiquement.
Cest dire qu lexcution, le compilateur applique la mthode afficher() pour chaque objet
selon sa propre dfinition.

 SOLUTION : les mthodes virtuelles !

VII.4 Fonctions membres virtuelles:


C++ offre un mcanisme qui permet de garantir que le polymorphisme provoquera lappel de la
version redfinie et non celui de la version hrite : cest la spcification du statut virtual.
Une mthode virtuelle est une mthode particulire invoque au moyen d'un pointeur ou d'une
rfrence sur une classe de base ; elle est lie dynamiquement au moment de l'excution.
L'instance invoque est dtermine par le type de classe de l'objet adress par le pointeur ou la
rfrence. La rsolution d'une mthode virtuelle est transparente l'utilisateur.
Il suffit de placer le mot rserv virtual devant la mthode que nous dsirons rendre virtuelle et le
tour est jou. Par ailleurs, il n'est pas ncessaire de dclarer virtuelles les mthodes redfinies dans
les classes drives, elles le sont automatiquement.

BEN ROMDHAN Mourad 77


LHERITAGE EN C++

class Point
{ int couleur;
public:
virtual void afficher() ;
virtual initialiser(int , int , int );
};
Deuxime utilisation pointeur de la classe mre qui pointe sur un objet de la classe drive:
void main()
{ Pointcol a(2,3,4);
Point *b=&a;
b->afficher();
}
Resultat de lexcution : (2,3) couleur=4
 appel de la mthode afficher() de la classe drive
Rfrence de la classe mre sur un objet de la classe drive:
void main()
{ Pointcol a(2,3,4);
Point &p=a;
p.afficher();
}
Resultat de lexcution : (2,3) couleur=4
 appel de la mthode afficher() de la classe drive
Remarque:
 A lexcution du programme principal le compilateur affecte suivant le type dobjet la
mthode afficher() adquate de faon dynamique.
 La redfinition dune fonction virtuelle nest pas obligatoire, do si la classe drive na pas
redfinie la mthode virtuelle, alors pendant "lappel dynamique" cest la fonction de la classe
mre (virtuelle) qui va tre excute.
 Le mot virtual se place uniquement dans la dclaration de la classe. Lorsque vous dfinissez
la mthode l'extrieur de la classe, vous ne devez plus re-spcifier le mot virtual devant la
signature de la mthode
 Lorsque nous avons une redfinition des destructeurs dans une hirarchie de classe qui
comporte des mthodes virtuelles, il est gnralement prfrable que les destructeurs fassent
galement partie des tables des adresses des mthodes virtuelles. En effet, lorsque nous
ralisons un delete sur un pointeur d'une classe de base, le bon destructeur est alors pris en
compte. Vous obtenez ce comportement en dclarant virtual le destructeur de la classe de base.

BEN ROMDHAN Mourad 78


LHERITAGE EN C++

 Lorsque qu'une mthode virtuelle est invoque l'intrieur d'un des constructeurs de la
hirarchie, c'est toujours la mthode virtuelle de la classe de base qui est sollicit. En effet,
puisque nous sommes en phase de cration, les tables des adresses des mthodes virtuelles
n'existent pas encore. Nous ne pouvons donc pas intgrer le polymorphisme sur un
constructeur, il faut que l'objet soit d'abord cr. Du coup, un constructeur ne peut jamais tre
virtuel.
VII.4 Les classes abstraites :
En POO, nous pouvons dfinir des classes destines non pas instancier des objets, mais
simplement donner naissance dautres classes par hritage. On dit quon a affaire des
classes abstraites.
En C++, nous pouvons toujours dfinir de telles classes, en dclarant des fonctions membres
virtuelles dont on ne prcise pas le contenu dans la classe de base. Seules les classes de base
possdent alors, ventuellement une description du corps de ces fonctions virtuelles. On les
appelle des fonctions virtuelles pures.
Une fonction virtuelle pure se dclare en remplaant le corps de la fonction par les symboles = 0.
Syntaxe :
class NomClasse
{
//
virtual TypeRetout nomFonction (liste des arguments)=0;
//
}

Remarque:

 Une classe comportant au moins une fonction virtuelle pure est considre comme abstraite et
il nest plus possible de dclarer des objets de son type.
 Une fonction dclare virtuelle pure dans une classe de base doit obligatoirement tre
redfinie dans une classe drive ou dclare nouveau virtuelle pure ; dans ce dernier cas, la
classe drive est aussi abstraite.
Exemple : Figure

Class Figure perimetre()


{ getNom()
Public:
virtual float perimetre()=0;
virtual float surface()=0;
virtual void dessiner();
UnRectangle
}
longueur : float
largeur : float

La classe figure est une abstraction. Un programme ne crera pas dobjet


Figure, mais des objets Rectangle, Cercle, etc..
On remarque que la fonction perimetre() introduite au niveau de la classe UnCarre
Figure nest pas un service rendu aux programmeurs mais une contrainte : cote : float
son rle nest pas de dire ce quest le primtre dune figure, mais
dobliger les futures classes drives de figure le dire.

BEN ROMDHAN Mourad 79


LHERITAGE EN C++

class Figure
{
public:
virtual float perimetre()=0;
virtual char * getNom()=0;
};
class UnRectangle:public Figure
{
float longueur;
float largeur;
public :
UnRectangle(float longueur,float largeur)
{
this->longueur=longueur;
this->largeur=largeur;
}
float perimetre()
{
return 2*(longueur+largeur);
}
char* getNom()
{
return "RECTANGLE";
}
};
class UnCarre:public UnRectangle
{
float cote;
public:
UnCarre(float cote):UnRectangle(cote,cote)
{
this->cote=cote;
}
float perimetre()
{
return cote*4;
}
char* getNom()
{
return "CARRE";
}
};
void main()
{
UnRectangle *Rect=new UnRectangle(10,5);
UnCarre *Carre=new UnCarre(10);
Figure *T[2];
T[0]=Rect;
T[1]=Carre;
for (int i=0;i<=1;i++)
{
cout<<"Le PERIMETRE DU "<<T[i]->getNom()<<" = "<<
T[i]->perimetre()<<endl;
}
}

BEN ROMDHAN Mourad 80


LHERITAGE EN C++

VII.5 Exemple dutilisation de fonctions virtuelles : liste htrogne :


Nous allons crer une classe permettant de grer une liste chaine dobjets de types diffrents et
disposant des fonctionnalits suivantes :
 ajout dun nouvel lment,
 affichage des valeurs de tous les lments de la liste,
 mcanisme de parcours de la liste.
Rappelons que, dans une liste chaine, chaque lment comporte un pointeur sur llment
suivant.

  
 Informations 1 Informations 2 Informations 3
tete

Mais ici lon souhaite que les diffrentes informations puissent tre de types diffrents. Aussi
cherchons-nous isoler dans une classe (nomm liste) toutes les fonctionnalits de gestion de la
liste elle-mme sans entrer dans les dtails spcifiques aux objets concerns. Nous appliquerons
alors ce schma :

  
   
tete

Objet 1 Objet 2 Objet 3

La classe liste est compose de :


- un pointeur sur llment suivant
- un pointeur sur linformation associe (en fait, ici, un objet).

struct element
{
element *suivant ;
mere *contenu ;
} ;
class Liste{
element *tete ;
public:
Liste() ;
~Liste() ;
void ajouter(mere *) ;
void afficher() ;
.....
};

BEN ROMDHAN Mourad 81


LHERITAGE EN C++

Exemple :
class Article
{
long code;
char nom[100];
double quantite;
double prix;
public:
virtual void afficher()
{
cout <<"code="<<code<<endl ;
cout <<"Nom="<<nom<<endl ;
cout <<"Quantit="<<quantite<<endl ;
cout <<"Prix unitaire="<<prix<<endl ;
}
virtual void saisir()
{
cout <<"code="<<endl; cin>>code ;
cout <<"Nom="<< endl ; cin >> nom ;
cout <<"Quantit="<< endl ; cin >> quantite ;
cout <<"Prix unitaire="<< endl ;cin>> prix ;
}
};
class Boissons:public Article
{
public:
double volume;
void afficher()
{
cout << "cest un boissons"<<endl
Article ::afficher() ;
cout << "volume="<<volume<<endl ;
}
void saisir()
{
cout << "saisie dun boissons"<<endl
Article ::saisir() ;
cout << "Volume= "<<endl ; cin>> volume ;
}
} ;
class Confiture: public Article{
public:
double poids;
void afficher()
{
cout << "cest un confiture"<<endl
Article ::afficher() ;
cout << "Poids="<<poids<<endl ;
}
void saisir()
{
cout << "cest un confiture"<<endl
Article ::saisir() ;
cout << "Poids= "<<endl ; cin>> poids ;
}
} ;

BEN ROMDHAN Mourad 82


LHERITAGE EN C++

typedef struct element


{
element *suivant ;
Article *contenu ;
} ;
class Liste
{
element *tete ;
public :
Liste()
{ tete=NULL ;}
~Liste()
{ delete tete ;}
void ajouter(Article *a) // au dbut de la liste
{
element *nouv= new element ;
nouv->contenu=a ;
nouv->suivant=tete ;
tete=nouv ;
}
void afficher()
{
elemnet *parc=tete ;
while(parc !=NULL)
{
parc->contenu->afficher() ;
parc=parc->suivant ;
}
}
} ;
void main()
{
Confiture c ;
c.saisir() ;
Boissons b ;
b.saisir() ;
Liste L ;
L.ajouter(&c ) ;
L.ajouter(&b ) ;
L.afficher() ; //affichage de toute la liste
}

VIII. Lhritage et la surcharge des oprateurs:


VIII.1 Surcharge des oprateurs simples:
Les oprateurs peuvent tre surchargs dans la classe de base et celle drive, et suivant le type de
lobjet loprateur correspondant va tre xcuter. Si la classe drive na pas dfinit loprateur
alors celui de la classe mre qui va participer dans lopration. Prenons lexemple de la classe
Point et Pointcol :
class Point
{ //...
Point operator + (Point p);
//...
};

BEN ROMDHAN Mourad 83


LHERITAGE EN C++

class Pointcol: public Point


{
//...
};
void main()
{
Pointcol a(1,2,3);
Pointcol b(4,5,6);
Point s;
s=a+b;//Ok, s(5,7)
}

Or dans ces conditions, si s est de type pointcol, une banale affectation telle que s=a+b ; sera
rjete par le compilateur, faute de disposer de la conversion de point en pointcol.
Si on souhaite dfinir la somme de deux points colors, il faudra redefinir loprateur + au sein de
pointcol.

VIII.2 Surcharge de loprateur daffectation:

Nous avons expliqu comment C++ dfinit laffectation par dfaut entre deux objets de meme
type. Dautre part, nous avons montr quil tait possible de surdfinir cet oprateur daffectation.
Supposons que la classe B hrite de la classe A

 La classe drive na pas surdfini loprateur=:

Laffectation de deux objets de type B se droule membre membre en considrant que la "partie
hrit de A" constitue un membre. Ainsi, les membres propre B sont traits par laffectation
prvue pour leur type. La partie hrit de A est traite par laffectation prvue dans la classe A,
cest--dire par loprateur= surdfini dans A sil existe, par laffectation par defaut sinon.
On retrouve un comportement tout fait analogue celui dcrit dans le cas du constructeur de
recopie.

 La classe drive a surdfini loprateur=:

Laffectation de deux objets de type B fera alors ncessairement appel loprateur= dfini dans
B. celui de A ne sera pas appel, mme sil a t surdfini. Il faudra donc que loprateur= de B
prenne en charge tout ce qui concerne laffectation dobjets de type B, y compris pour ce qui est
des membres hrits de A.
class Point
{
protected:
int x;
int y;
public:
Point &operator = (const Point &p)
{ x=p.x;
y=p.y;
cout << oprateur = de point\n;
return *this;
}
};

BEN ROMDHAN Mourad 84


LHERITAGE EN C++

class Pointcol: public Point


{ int col;
public:
Pointcol(int abs,int ord,int c);
Pointcol &operator = (const Pointcol &p)
{ x=p.x; y=p.y; col=p.col ;
cout << oprateur = de pointcol\n;
return *this;
}
};
void main()
{
Pointcol a(1,2,3);
Pointcol b(4,5,6);
b=a; //b=(1,2,3)
}

Dans cet exemple loprateur = dfini dans la classe point na pas t appel lors dune affectation
entre objets de type poincol.
Le problme est voisin de celui rencontr propos du constructeur de recopie. Donc si lon veut
pouvoir profiter de loprateur = dfini dans la classe point, il faudra lappeler explicitement :

Pointcol &operator = (const Pointcol &p)


{
Point *p1, *p2 ;
cout << oprateur = de pointcol\n;
p1=this ;
p2=(Point*)&p ;
*p1=*p2 ;//affectation de la "partie point" de p
col=p.col ;
return *this;
}

Nous convertissons les pointeurs (this et &p) sur des objets de pointcol en des pointeurs de type
point. Il suffit ensuite de raliser une affectation entre les nouveaux objets points (*p1 et *p2)
pour entraner lappel de loprateur = de la classe Point.

BEN ROMDHAN Mourad 85

S-ar putea să vă placă și