Vous êtes sur la page 1sur 30

Cours P.O.O.

ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

CHAPITRE 5

HÉRITAGE & POLYMORPHISME


EN C++

Faten BEN HMIDA Université de La Manouba – ENSI – 2012/2013

Plan
2

Héritage
Principe de l’héritage
Visibilité des membres
Redéfinition des membres
Constructeur et destructeur
Constructeur de recopie et héritage
Opérateur d’affectation et héritage
Types d’héritage
Héritage multiple

Faten Ben Hmida 1


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Plan
3

Polymorphisme
Compatibilité classe de base / classe dérivée
Typage statique / Typage dynamique
Fonctions virtuelles
Destructeur virtuel
Fonctions virtuelles pures et classes abstraites
Structures polymorphes
L’opérateur typeid

Principe de l’héritage
4

principe de base de la P.O.O. qui permet de définir une nouvelle


classe à partir d’une classe existante.
Spécialisation

Classe de base Classe dérivée


Classe mère Classe fille
Super classe Sous classe

Généralisation

2 types d’héritage :
Héritage simple : la classe dérivée ne peut hériter que d’une seule
classe de base.
Héritage multiple : la classe dérivée peut hériter de plusieurs classes
de base.

Faten Ben Hmida 2


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Principe de l’héritage
5

La classe dérivée hérite des attributs et des méthodes de la


classe de base avec la possibilité de :
Ajouter des attributs spécifiques.
Ajouter des méthodes spécifiques.
Redéfinir des méthodes héritées.

Dans le cas de la redéfinition d’une méthode par une classe


dérivée, c’est cette dernière qui sera appelée pour un objet issu
de la classe dérivée et non la méthode de la classe de base.

La classe dérivée peut accéder à tous les membres (attributs et


méthodes) publics et protégés (protected) de sa classe de base.

Principe de l’héritage
6

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

Des constructeurs (par défaut, avec paramètres, de recopie)


Du destructeur
De l’opérateur d’affectation
Des relations d’amitié

Faten Ben Hmida 3


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Principe de l’héritage
7

Etant donnée la classe Point permettant de manipuler des points


et définie comme suit :
class Point
{ int x, y;
public :
void initialiser(int, int);
void deplacer(int, int);
void afficher();
};

Nous souhaitons définir une nouvelle classe PointCol afin de


manipuler des points colorés. Cette classe aura les mêmes
fonctionnalités de la classe Point avec la possibilité d’attribuer
une couleur aux objets de type PointCol.

Principe de l’héritage
8

La classe PointCol est donc une classe dérivée de la classe de


base Point. Elle est définie comme suit :
class PointCol : public Point
{ Héritage public / Dérivation
short c; publique : tous les membres
public :
publics de la classe de base
void colorer(short co) Point seront des membres
{ c = co; } publics de la classe dérivée
PoinCol
};

Méthode spécifique Attribut spécifique

Faten Ben Hmida 4


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Principe de l’héritage
9

Chaque objet de type PointCol peut alors faire appel :


aux méthodes publiques de PointCol (colorer)
aux méthodes publiques de Point (initialiser, deplacer, afficher)
#include <iostream>
#include "Point.h" //importation de la déclaration de Point
using namespace std;
class PointCol : public Point //PointCol dérive de Point
{ short couleur;
public :
void colorer (short cl) { couleur = cl; }
};
int main()
{ pointcol p;
p.initialiser(10,20);
p.colorer(5);
p.afficher();
p.deplacer(2,4); Coordonnees : 10 20
p.afficher(); Coordonnees : 12 24
}

Visibilité des membres


10

Accès aux membres publics

La classe dérivée peut accéder à tous les membres publics de sa


classe de base.

Les membres publics de la classe de base restent visibles pour


les utilisateurs de sa classe dérivée.

Faten Ben Hmida 5


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Visibilité des membres


11

Accès aux membres privés


La classe dérivée et ses utilisateurs ne peuvent pas accéder aux
membres privés de la classe de base.
class PointCol : public Point
{ short couleur; x et y sont deux membres privés de
public : la classe de base Point, il sont donc
void colorer (short cl) définitivement inaccessibles à
{ couleur = cl; } l’extérieur de Point, même pour ses
void afficher_c(); classes dérivées. Donc, les méthodes
}; de PointCol ne peuvent pas accéder
void PointCol::afficher_c()
à x et y.
{
cout << "Coordonnees : " << x << "," << y << endl;
cout << "Couleur : " << couleur << endl;
}

Visibilité des membres


12

Accès aux membres privés


La classe dérivée peut accéder aux membres privés de sa classe
de base en passant par les méthodes publiques de cette dernière.
class PointCol : public Point
{ short couleur;
public :
void colorer (short cl)
{ couleur = cl; }
L’affichage de x et y se fait en
void afficher_c(); passant par la méthode publique
}; afficher() de la classe Point.
void PointCol::afficher_c()
{
afficher();
cout << "Couleur : " << couleur << endl;
}

Faten Ben Hmida 6


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Visibilité des membres


13

Membres protégés
Niveau de visibilité protected : les membres protégés de la
classe de base sont accessibles à ses classes dérivées mais pas à
ses utilisateurs.
class Point x et y sont des attributs
{
protégés de la classe Point.
protected:
Ils sont donc accessibles
int x, y;
public : aux classes dérivées de
void initialiser(int, int); Point mais pas aux
void deplacer(int, int); utilisateurs de la classe
void afficher(); Point ni aux utilisateurs de
}; ses classes dérivées.

Visibilité des membres


14

Membres protégés
class PointCol : public Point
{ short couleur; L’accès aux attributs x et y est
public : possible à partir de la classe
void colorer (short cl) PointCol car cette dernière est une
{ couleur = cl; }
classe dérivée de la classe point. On
void afficher_c();
};
n’a plus besoin de passer par les
void PointCol::afficher_c() méthodes publiques pour ce faire.
{
cout << "Coordonnees : " << x << "," << y << endl;
cout << "Couleur : " << couleur << endl;
}

Faten Ben Hmida 7


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Visibilité des membres


15

Récapitulation
class A class B : public A int main()
{ { {
private: public: A a;
int i; void f() a.i = 0;
public: { a.j = 0;
int j; i = 0; a.k = 0;
protected: j = 0; }
int k; k = 0;
}; }
};

Redéfinition des méthodes


16

Les méthodes de la classe de base peuvent être modifiées dans


la classe dérivée tout en gardant le même nom, on parle de
redéfinition de méthodes.

L’appel de cette méthode à partir d’un objet issu de la classe


dérivée provoque l’appel de la méthode redéfinie et non de la
méthode originale de la classe de base.

La méthode de la classe de base reste accessible via l’opérateur


de résolution de portée :: précédé par le nom de la classe de
base.

Faten Ben Hmida 8


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Redéfinition des méthodes


17

class Point void PointCol::afficher()


{ protected: { Point::afficher();
int x, y; Méthodes redéfinies cout<<"Couleur: "<<couleur<<endl;
public : }
void initialiser(int, int); void PointCol::initialiser(int abs,
void deplacer(int, int); int ord, short cl)
void afficher(); { Point::initialiser(abs,ord);
}; couleur = cl ;
... }
Appels aux méthodes
int main()
class PointCol : public Point
{ PointCol p;
de la classe de base
{ short couleur; p.initialiser(10,20,5);
public: p.afficher() ;
void colorer(short cl) p.point::afficher();
{ couleur = cl; } p.deplacer(2,4);
void afficher(); p.afficher();
p.colorer(2);
void initialiser(int,int,short); p.afficher();
Appels aux
}; } méthodes redéfinies

Redéfinition des méthodes


18

Redéfinition et surdéfinition (surcharge)


Que se passe-t-il lorsqu’une méthode surdéfinie (surchargée)
dans la classe de base est redéfinie dans la classe dérivée ?
int main()
class A { int n; char c; A a; B b;
{ public: a.f(n); // appelle A::f(int)
void f(int n) a.f(c); // appelle A::f(char)
{ ... } b.f(n); // appelle B::f(float)
void f(char c) b.f(c); // appelle B::f(float)
{ ... } }
};
class B : public A Pour les appels b.f(n) et b.f(c) le compilateur n’a
{ public: considéré que la fonction f de B qui s’est trouvée
void f(float x)
appelée dans les deux cas. Si aucune fonction f
{ ... }
}; n’avait été définie dans B, les fonctions f(int) et
f(char) de A auraient été appelées.

Faten Ben Hmida 9


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Redéfinition des méthodes


19

Redéfinition et surdéfinition (surcharge)


class A
{ public: int main()
void f(int n) { int n; char c; B b;
{ ... } b.f(n); // erreur de compilation
void f(char c) b.f(c); // erreur de compilation
{ ... } }
};
class B : public A
{ public: Pour les appels b.f(n) et b.f(c), le compilateur
void f(int n, int p) n’a considéré que l’unique fonction f(int,int)
{ ... } de B, qui ne convient pas dans les deux cas.
};

Lorsqu’une méthode est redéfinie dans une classe dérivée, elle


masque toutes les méthodes de même nom de la classe de base.

Redéfinition des attributs


20

La redéfinition s'applique aussi sur les attributs, bien qu’elle soit


très peu courante.
class A class B : public A
{ int n; { float n;
char c; ...
... };
};

Dans ce cas, si b est un objet de type B, b.n désigne l’attribut n


de type float de b. Il sera toujours possible d'accéder à
l’attribut n de type int (hérité de A) par b.A::n

Remarque : l’attribut n défini dans B s'ajoute au membre n hérité


de A ; il ne le remplace pas.

Faten Ben Hmida 10


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Constructeur et destructeur
21

Rappel des règles d’appel du constructeur (sans héritage)


Il n’existe aucun constructeur : le compilateur génère un
constructeur par défaut n’admettant aucun argument. Lors de la
création d’un objet, il ne sera fourni aucun paramètre.

Il existe au moins un constructeur : toute création d’objet entraîne


l’appel d’un constructeur choisi en fonction des paramètres
fournis. Si aucun constructeur ne convient, il y a erreur de
compilation.

Il existe un destructeur : ce dernier sera appelé avant la


destruction de l’objet.

Constructeur et destructeur
22

Hiérarchisation des appels en cas d’héritage


Lors de la création d’un objet d’une classe dérivée, les
constructeurs sont appelés dans l’ordre suivant :

Les constructeurs des objets attributs de la classe de base


Le constructeur de la classe de base
Les constructeurs des objets attributs de la classe dérivée
Le constructeur de la classe dérivée

Les destructeurs sont appelés dans l’ordre inverse des


constructeurs.

Faten Ben Hmida 11


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Constructeur et destructeur
23

Hiérarchisation des appels en cas d’héritage


class A { int main()
public : {
A() { cout << "A::A()" << endl; } B b;
~A() { cout << "A::~A()" << endl; } cout << "**" << endl;
}; }
class B : public A { A::A()
public : B::B()
B() { cout << "B::B()" << endl; } **
~B() { cout << "B::~B()" << endl; } B::~B()
}; A::~A()
Pour créer un objet de type B, un objet de type A doit d’abord être créé, donc le
constructeur de A sera appelé, puis complété par ce qui est spécifique à B par
appel au constructeur de B. La même démarche s'applique aux destructeurs : lors
de la destruction d'un objet de type B, il y aura automatiquement appel du
destructeur de B, puis appel de celui de A.

Constructeur et destructeur
24

Hiérarchisation des appels en cas d’héritage


class A class D : public C
{ public: { A att2;
A() {cout<<"A()\n";} B att3;
public:
};
D() {cout<<"D()\n";}
class B
};
{ public:
B() {cout<<"B()\n";} int main()
}; {
class C D obj1; B()
{ B att1; } C()
public: A()
C() {cout<<"C()\n";} B()
}; D()

Faten Ben Hmida 12


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Constructeur et destructeur
25

Transmissions d’informations entre constructeurs


Jusque là on a vu comment se déroulent les appels aux
constructeurs de manière automatique lorsque ces derniers ne
possèdent pas d’arguments.

La situation est différente lorsque le(s) constructeur(s) de la


classe de base admet(tent) des arguments.

C++ a prévu la possibilité de spécifier, dans la définition d'un


constructeur d'une classe dérivée, les informations que l'on
souhaite transmettre à un constructeur de la classe de base.

Constructeur et destructeur
26

Transmissions d’informations entre constructeurs


class Point class PointCol : public Point
{ ... { ...
public: public:
Point (int, int); PointCol (int, int, short);
~Point (); ~PointCol();
... ...
}; } ;

Si l'on souhaite que PointCol transmette à Point les deux premiers paramètres
reçus, on écrira son en-tête de cette manière :
PointCol (int abs, int ord, short cl) : Point (abs, ord)
Le compilateur mettra en place la transmission au constructeur de Point des
paramètres abs et ord correspondant aux deux premiers arguments de PointCol.
Ainsi, la déclaration : PointCol a (10, 15, 3) ; entraînera :
• l'appel de Point qui recevra les arguments 10 et 15
• l'appel de PointCol qui recevra les arguments 10, 15 et 3

Faten Ben Hmida 13


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Constructeur de recopie et héritage


27

Rappel
Le constructeur de recopie est appelé en cas :
d'initialisation d'un objet par un objet de même type.
de transmission de la valeur d'un objet en argument.

En cas d’héritage
2 cas se présentent :
La classe dérivée ne définit pas de constructeur de recopie.
La classe dérivée définit un constructeur de recopie.

Constructeur de recopie et héritage


28

Soit l’exemple suivant :

class A { ... }; // A classe de base


class B : public A { ... }; // B classe dérivée de A
void fct(B); // fct : fonction recevant un argument de type B
...
B b1(...); // appel d’un constructeur usuel
fct(b1); // appel de fct avec transmission de b1 par valeur
// donc appel du constructeur de recopie de la classe B

Faten Ben Hmida 14


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Constructeur de recopie et héritage


29

Constructeur de recopie non défini dans la classe dérivée


Il y a donc appel du constructeur de recopie par défaut de B
pour les attributs spécifiques.

Les attributs de B hérités de la classe A seront copiés par appel


au constructeur de recopie de A.

Si A a défini un constructeur de recopie il sera appelé.

Si A n’a pas défini de constructeur de recopie, il y aura appel du


constructeur de recopie par défaut.

Constructeur de recopie et héritage


30

Constructeur de recopie non défini dans la classe dérivée


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 spécifique. C++ ne prévoit pas l’appel automatique du
constructeur de recopie de A lorsque celui de B est défini.

Il reste possible d’utiliser le mécanisme de transmission


d’informations entre constructeurs :
B (const B& x) : A(x) // x de type B est converti dans le
type
// A pour être transmis au constructeur
// de recopie de A
{
// recopie de la partie de x spécifique à B
}

Faten Ben Hmida 15


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Opérateur d’affectation et héritage


31

Rappel
L’affectation d’objets s’effectue membre à membre de manière
superficielle. Pour effectuer une copie profonde (en cas de
présence d’attributs dynamiques), l’opérateur = est surchargé
sous la forme d’une méthode de la classe concernée.

En cas d’héritage
2 cas se présentent :
La classe dérivée ne surcharge pas l’opérateur d’affectation.
La classe dérivée surcharge l’opérateur d’affectation.

Opérateur d’affectation et héritage


32

Opérateur = non surchargé dans la classe dérivée


L’affectation de deux objets de type B se déroule membre à
membre pour les attributs spécifiques.

Les attributs hérités de A sont traités par l’affectation prévue


dans la classe A, c’est-à-dire :

Par l’opérateur = surchargé dans A s’il existe.

Par l’affectation par défaut de A si l’opérateur = n’a pas été


surchargé.

Faten Ben Hmida 16


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Opérateur d’affectation et héritage


33

Opérateur = surchargé dans la classe dérivée


L'affectation de deux objets de type B fera alors nécessairement
appel à l'opérateur = surchargé dans B.

L’opérateur d’affectation de A ne sera pas appelé, même s'il a


été surchargé dans cette dernière.

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 les
attributs hérités de A.

Types d’héritage
34

Il existe trois différents types d’héritage : public, private et


protected.

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


class pointcol : public point

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


class pointcol : point class pointcol : private point

Le Type d’héritage conditionne la visibilité entre classe de base


et classe dérivée.

Faten Ben Hmida 17


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Types d’héritage
35

Héritage public
class ClasseDerivee : public ClasseBase

Statut du membre dans la classe de Statut du membre dans la classe


base dérivée
private Inaccessible
protected protected
public public

Tous les membres de la classe de base conservent dans la classe


dérivée le statut qu'ils avaient.

Types d’héritage
36

Héritage privé
class ClasseDerivee : private ClasseBase

Statut du membre dans la classe de Statut du membre dans la classe


base dérivée
private Inaccessible
protected private
public private

Les membres public et protected de la classe de base deviennent


private dans la classe dérivée et donc inaccessibles aux classes
dérivées de cette dernière.

Faten Ben Hmida 18


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Types d’héritage
37

Héritage protégé
class ClasseDerivee : protected ClasseBase

Statut du membre dans la classe de Statut du membre dans la classe


base dérivée
private Inaccessible
protected protected
public protected

Les membres public de la classe de base deviennent protected


dans la classe dérivée et donc inaccessibles aux utilisateurs de
cette dernière.

Héritage multiple
38

C++ permet de mettre en œuvre l’héritage multiple.

Dans ce type d’héritage, une classe peut être dérivée de plus


d’une classe de base.

Il s’agit d’une forme d’héritage dont l’utilisation est peu


répandue vu les contraintes qu’elle impose et des situations de
conflits qu’elle génère.

Exemple : une classe C hérite de deux classes de base A et B.


class C : public A, public B { ... };

Faten Ben Hmida 19


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Héritage multiple
39

class point class couleur


Exemple { int x, int y; { short cl;
public : public :
point (int , int ); couleur (int);
~point (); ~couleur();
afficher(); afficher();
}; };
Class pointcol : public point, public couleur
{
Héritage multiple
public :
pointcol(int a, int b, int c) : point(a, b), couleur(c)
{ ... }
~pointcol();
void afficher() Redéfinition d’une méthode
{ point::afficher();
couleur::afficher();
Transmission d’information aux constructeurs
}
des classes de base
};

Héritage multiple
40

Les classes de bases peuvent avoir des membres (attributs ou


méthodes) ayant le même identifiant.

class A class B class C : public A, public B


{ protected : { protected : {
int x; int x; ...
... ... };
}; };

Dans la classe dérivée C qui hérite de deux attributs ayant le


même identifiant, l’ambiguïté doit être levée en utilisant
l’opérateur de résolution de portée :: (A::x et B::x)

Faten Ben Hmida 20


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Héritage multiple
41

Soit la hiérarchie de classes définie comme suit :


class A
{ int x, y;
...
};

class B : public A { ... };

class C : public A { ... };

class D : public B, public C


{ ...
};

Pb : dans la classe D les attributs x et y seront dupliqués car


hérités doublement de B et de C.

Héritage multiple
42

On peut profiter des deux jeux de données, dans ce cas on


fera la distinction en utilisant l’opérateur de résolution de
portée : A::B::x et A::C::x et si B et C ne redéfinissent pas
x simplement : B::x et C::x.

Si on veut interdire la duplication des attributs hérités de A, on


utilisera l’héritage virtuel comme suit :
class B : public virtual A { ... };
class C : public virtual A { ... };
class D : public B, public C { ... };

définir A comme virtual dans la déclaration de B signifie que


les membre de A ne devront être incorporés qu'une seule fois
dans les descendants éventuels de B.

Faten Ben Hmida 21


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Compatibilité classes de base/dérivée


43

La compatibilité entre classe dérivée et sa classe de base se


résume à l’existence de conversions implicites :

d'un objet de la classe dérivée dans un objet de la classe de base

d'un pointeur sur la classe dérivée en un pointeur sur la classe de


base.

La conversion de la classe de base vers la classe dérivée n’est


pas autorisée.

Compatibilité classes de base/dérivée


44

Conversion d’objets
Soit les deux classes Point et PointCol qui en dérive et les
déclarations :
Point a;
PointCol b;
l'affectation a = b est légale. Elle entraîne une conversion de b
dans le type Point et l'affectation du résultat à a.

Cette affectation se fait soit par appel de l'opérateur


d'affectation de la classe Point si celui-ci a été surdéfini, soit par
emploi de l'affectation par défaut.

L'affectation b = a sera rejetée.

Faten Ben Hmida 22


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Compatibilité classes de base/dérivée


45

Conversion de pointeurs
Soit les deux classes Point et PointCol qui en dérive et les
déclarations :
Point* adp;
PointCol* adpc;
l'affectation adp = adpc est légale. Elle correspond à une
conversion du type PointCol* dans le type Point*.

L'affectation adpc = adp sera naturellement rejetée. Cependant


il est possible de recourir à un cast (adpc = (PointCol *) adp)

Même si on peut forcer la conversion de pointeurs de la classe de base vers la


classe dérivée, la conversion d’objets dans ce même sens reste impossible.

Compatibilité classes de base/dérivée


46

Limitations
Soit les déclarations suivantes :
Point p(3, 5); PointCol pc(8, 6, 2);
adp = &p; adpc = &pc;

A ce niveau, l'instruction adp->afficher() appellera la méthode


Point::afficher(), tandis que l'instruction adpc->afficher()
appellera la méthode PointCol::afficher().

Faten Ben Hmida 23


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Compatibilité classes de base/dérivée


47

Limitations
Suite à cette affectation : adp = adpc;

Que provoque l’instruction adp->afficher() ?


adp est de type Point et l’objet pointé est de type PointCol.
Le compilateur appellera donc la méthode Point::afficher()
correspondante au type du pointeur.
L’appel adp->colorer(8) sera rejeté par le compilateur.

Compatibilité classes de base/dérivée


48

Limitations
Nous pouvons désigner, à l'aide d'un même pointeur, des objets
de types différents, mais nous n'avons pour l'instant aucun moyen
de tenir réellement compte du type de l'objet pointé.

Le type des objets pointés est décidé et figé au moment de la


compilation : "typage statique".

En réalité, C++ permet d'effectuer cette identification d'un objet


au moment de l'exécution (et non plus à la compilation) et de
réaliser ce que l'on appelle le "typage dynamique" (forme de
polymorphisme). Cela nécessitera l'emploi de fonctions virtuelles.

Faten Ben Hmida 24


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Fonctions virtuelles
49

Le mécanisme des fonctions virtuelles proposé par C++ permet


de faire en sorte que l'instruction adp->afficher() appelle non
plus systématiquement la méthode afficher() de Point, mais
celle correspondant au type de l'objet réellement pointé par adp
(qui peut être Point ou PointCol).

Pour ce faire, il suffit de déclarer "virtuelle" (mot clé virtual) la


méthode afficher() de la classe Point.
class Point
{ .....
virtual void afficher();
.....
};

Fonctions virtuelles
50

La fonction virtuelle est utilisée afin de permettre l'utilisation


d'objets de façon appropriée dans un programme où le type des
objets n'est pas connu à la compilation.

Le mot clé virtual indique que la fonction peut avoir différentes


versions pour les différentes classes dérivées.

Lors de l’exécution du programme, la bonne version de fonction


est exécutée dynamiquement typage (ou liaison) dynamique

Faten Ben Hmida 25


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Fonctions virtuelles
51

Exemple 1
class Point
{ protected:
int x, y;
public:
Point(int abs=0, int ord=0)
{ x=abs; y=ord; }
virtual void afficher()
{ cout<<"Je suis un point de coordonnees : "<<x<<" "<<y<<"\n"; }
};
class PointCol : public Point
{ short couleur;
public:
PointCol(int abs=0,int ord=0,short cl=1) : Point (abs, ord)
{ couleur = cl; }
void afficher()
{ cout<<"Je suis un point colore de coordonnees : "<<x<<" "<<y;
cout<<" et de couleur : "<<couleur<< "\n";
}
};

Fonctions virtuelles
52

Exemple 1
int main()
{ Point p(3,5); Point* adp = &p;
PointCol pc(8,6,2); PointCol* adpc = &pc;
adp->afficher();
adpc->afficher();
cout << "-----------------\n" ;
adp = adpc;
adp->afficher();
adpc->afficher();
}

Je suis un point de coordonnees : 3 5


Je suis un point colore de coordonnees : 8 6 et de couleur : 2
-----------------
Je suis un point colore de coordonnees : 8 6 et de couleur : 2
Je suis un point colore de coordonnees : 8 6 et de couleur : 2

Faten Ben Hmida 26


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Fonctions virtuelles
53

Exemple 2
class ObjetGeo
{ Position P; ObjetGeo *ptr;
public: ptr = new Sphere(7);
ObjetGeo(); ptr->afficher();
virtual void afficher()
{ cout << "Position " << P << endl; }
. . . ptr->afficher() provoque
}; l’appel de la méthode
afficher() redéfinie de la
class Sphere : public ObjetGeo
{ float rayon; classe Sphere malgré que
public: le pointeur ptr est à la base
Sphere(int r); est pointeur de type
void afficher()
ObjetGeo. Il s’agit du
{ ObjetGeo::afficher();
cout << "Rayon: " << rayon << endl; typage dynamique réalisé
} grâce à l’utilisation de la
}; virtualité.

Fonctions virtuelles
54

Restrictions
Seules les méthodes d’une classe peuvent être virtuelles.

Le constructeur ne peut pas être virtuel.

Le destructeur peur être virtuel.

L’opérateur d’affectation est une méthode particulière qui ne


peut pas être virtuelle.

Faten Ben Hmida 27


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Destructeur virtuel
55

Contexte :
Destruction d’un objet de la classe dérivée, qui est référencé par
un pointeur de la classe de base.

Résultat :
Si le destructeur de la classe de base est virtuel
alors le destructeur de la classe dérivée sera appelé d’abord,
celui de la classe de base sera appelé ensuite.
Sinon seul le destructeur de la classe de base est appelé.

Destructeur virtuel
56

Exemple :
ObjetGeo *OB = new Cube();
class ObjetGeo class ObjetGeo
{ public: { public:
~ virtual ObjetGeo(); ~ ObjetGeo();
... ...
}; };

class Cube : public ObjetGeo class Cube : public ObjetGeo


{ public: { public:
~ Cube(); ~ Cube();
}; };

delete OB;

Le destructeur de Cube est appelé Seul le destructeur de ObjetGeo


en premier, le destructeur de sera appelé.
ObjetGeo sera appelé ensuite.

Faten Ben Hmida 28


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Fonction virtuelle pure et classe abstraite


57

Certaines classes représentent des concepts abstraits par


lesquels des objets ne peuvent pas exister.

Exemple : Un objet géométrique ne peut exister sans définir


comment sa forme est représentée.

On déclare des fonctions virtuelles pures par un initialisateur = 0.

Une classe qui possède une fonction virtuelle pure est une classe
abstraite et ne peut donc pas être instanciée.

Les classes dérivées implémentent à leur niveau ces fonctions.

Fonction virtuelle pure et classe abstraite


58

class ObjetGeo
Classe abstraite
{ protected:
Position P; Fonction virtuelle pure
public:
. . .
virtual float volume() = 0;
};

class Cube : public ObjetGeo class Sphere : public ObjetGeo


{ float Larg, Long, Haut; { int rayon;
public: public:
float volume() float volume()
{ return (Larg*Long*Haut); { return ((4/3)*rayon^3*PI);
} }
}; };
Implémentations de la fonction
virtuelle pure

Faten Ben Hmida 29


Cours P.O.O. ENSI - II1
Chapitre 5 : Héritage & Polymorphisme en C++ A.U. : 2012/2013

Structure polymorphe
59

Collection d’objets d’une classe de base comportant des


méthodes virtuelles.
ObjetGeo *Tab[N];

Tab est rempli d’objets de type Sphere et/ou Cube

float Somme=0;
for(i=0; i<N; i++)
Somme += Tab[i]->volume();

L’opérateur typeid
60

l’opérateur typeid permet de connaître le type d’un objet lors


de l’exécution

ObjetGeo *Tab[N];
int nbCube = 0;
for(i = 0; i < N; i++)
if ( typeid(Tableau[i]) == typeid(Cube))
nbCube++;

Faten Ben Hmida 30

Vous aimerez peut-être aussi