Polymorphisme Polymorphisme
Département Informatique
Programmation objet
Cours n°11
Le polymorphisme d'héritage
Polymorphisme Polymorphisme
Département Informatique
Le Polymorphisme des classes
Une instance d ’une classe dérivée contient en interne une instance de sa classe de base.
On peut donc considérer qu ’une instance de la classe dérivée peut être utilisée à la place
d ’une instance de la classe de base.
Une instance d ’une classe dérivée F de M peut être vue sous deux formes :
•L ’instance est du type F
•L ’instance est du type M
On appelle ceci du polymorphisme.
Polymorphisme Polymorphisme
Département Informatique
Exemple :
Class M {}; class F:public M {};
void fonc(M&);
M m1;
F f1;
fonc(m1); // ok, m1 est du type M fonc(f1); // ok, f1 est du type F dérivé de M L ’instance f1 est vue ici comme étant de type M.
Bien entendu, les propriétés spécifiques de f1 ne peuvent pas être utilisées par fonc.
Polymorphisme Polymorphisme
Département Informatique La conversion de f1 en un objet de type M est donc ici implicite.
Elle est décidée par le compilateur dès la
compilation et pour toute la durée du programme.
On parle donc ici de polymorphisme statique.
Le polymorphisme fonctionne uniquement dans un seul sens :
• Une instance de la classe dérivée peut remplacer une instance de la classe de base
• Un pointeur (ou une référence) sur une instance de la classe dérivée peut remplacer un pointeur
(référence) sur une instance de la classe de base.
Mais pas le contraire !
Polymorphisme Polymorphisme
Département Informatique Les limites du polymorphisme statique
Considérons l ’exemple suivant :
#include <iostream>
using namespace std;
class M {
int a;
public:
M(int a1=0) : a(a1) {};
int get_a()
{ cout << "M::get_a() \n";
return a;}
};
Polymorphisme Polymorphisme
Département Informatique
class F : public M {
int a;
public :
F(int a1=0) : a(a1) {};
//--- appel automatique à M() int get_a()
{ cout << "F::get_a() \n";
return a;}
};
void f(M &m) { cout << m.get_a()
<< '\n';}
Polymorphisme Polymorphisme
Département Informatique
void main() { M m1(3);
F f1(4);
f(m1);
f(f1);
cout<<f1.get_a();
}
Le programme affichera : M::get_a() 3 M::get_a() 0 F::get_a() 4 car l’appel de f(f1) exécute la méthode M::get_a() et f1.M::a=0, alors que la dernière ligne emploie F : :get_a() et que f1.a=4
Polymorphisme Polymorphisme
Département Informatique
On voit qu ’un problème se pose :
Le compilateur décidant que f1 est du type M pour l ’appel de la fonction, c ’est à chaque fois M::get_a() qui est appelée, alors que
get_a a été redéfinie dans F.
C ’est très gênant, puisqu ’on perd
l ’intérêt de pouvoir spécialiser une classe dérivée.
Il faudrait que, lors de l ’exécution de f(), le
compilateur choisisse quelle est la méthode get_a à appeler en fonction du type réel de l ’instance utilisée.
Polymorphisme Polymorphisme
Département Informatique
Polymorphisme dynamique
Dans ce cas, le compilateur ne décide du type réel de l ’instance (dérivée ou base) que lors de l ’exécution.
Pour que ceci soit possible, la méthode doit être déclarée comme étant virtuelle (mot-clé
virtual)
Si l ’on reprend l ’exemple précédent, en indiquant que M::get_a est
virtuelle :
virtual int get_a { //.. Code identique }
Polymorphisme Polymorphisme
Département Informatique
void main() { M m1(3);
F f1(4);
f(m1);
f(f1);
cout<<f1.get_a();
}
Le programme affichera : M::get_a() 3 F::get_a() 4 F::get_a() 4 La bonne fonction est appelée, grâce à son statut virtuel.
Polymorphisme Polymorphisme
Département Informatique
ATTENTION :
• Une propriété membre d’une classe ne peut pas être virtuelle.
• Un membre statique d’une classe ne peut pas être virtuel.
• Les constructeurs ne peuvent pas être virtuels
• Les destructeurs peuvent être définis virtuels.
• Quand on utilise une fonction globale, l’effet virtuel n’est plus appliqué lorsque le paramètre est passé par valeur
• Le statut virtuel est conservé par l ’héritage.
• Une fonction virtuelle peut être redéfinie : elle reste virtuelle
• Une fonction virtuelle peut être surchargée : elle perd son statut virtuel puisqu'il s'agit d'une autre fonction.
Polymorphisme Polymorphisme
Département Informatique
Classes abstraites
Dans une hiérarchie d ’héritage, il est fréquent que les classes de bases ne soient jamais instanciées.
Elles fournissent alors uniquement un canevas pour le développement de classes dérivées.
On parle pour de telles classes de base de classes abstraites.
On ne peut pas construire d ’instances d ’une classe abstraite. Cela n'aurait d'ailleurs pas de sens.
Polymorphisme Polymorphisme
Département Informatique
Exemple : définition de formes géométriques Toutes les formes possèdent des propriétés
communes. (ex : origine)
Toutes les formes géométriques possèdent une surface, mais son expression ne peut pas être
généralisée.
Il est donc intéressant de définir une classe abstraite FORME et de dériver des classes
RECTANGLE, CERCLE, etc...
Il est en effet inutile de créer un objet de type FORME.
Polymorphisme Polymorphisme
Département Informatique
En C++, une classe est abstraite si elle contient au moins une fonction virtuelle pure.
Une fonction virtuelle pure est virtuelle, mais ne contient aucun code. Elle ne peut donc pas être appelée directement, mais doit être redéfinie dans
les classes dérivées.
La syntaxe est :
virtual <retour> fonc(<params>) = 0;
Si la fonction n ’est pas redéfinie dans la classe dérivée, celle-ci reste abstraite. Elle n'est donc
pas instanciable.
Polymorphisme Polymorphisme
Département Informatique
Exemple utilisation polymorphisme
Nous allons écrire les classes permettant de gérer des personnes.
Toutes les personnes gérées sont des humains.
Certaines sont des hommes, d ’autres des femmes.
Soit la hiérarchie de classes suivante : Hu
Ho Fe
Polymorphisme Polymorphisme
Département Informatique
class Humain {
private:
std::string Nom,Prenom;
//….
public:
Humain(std::string n,std::string p):
Nom(n),Prenom(p){};
virtual std::string DonneGenre() const =0;
void Affiche();
};
La classe contenant une fonction virtuelle pure, elle est abstraite.
Polymorphisme Polymorphisme
Département Informatiqueclass Femme:public Humain {
public:
std::string DonneGenre() const { return "Féminin";}
Femme(std::string n,std::string p) :Humain(n,p){}
class Homme:public Humain};
{
public:
std::string DonneGenre() const { return "Masculin";}
Homme(std::string n,std::string p) :Humain(n,p);
}; Ces deux classes définissent la méthode DonneGenre : elles ne sont pas abstraites.
Polymorphisme Polymorphisme
Département Informatique
La méthode Humain::Affiche n ’a pas besoin d ’être redéfinie dans les classes dérivées.
void Humain::Affiche() {
cout<<Nom<<Prenom;
}
Cette méthode pourra être utilisée avec Femme ou Homme. Si l ’on désire la redéfinir, il faudra appeler
Humain::Affiche() pour l ’affichage de Nom et Prenom
Polymorphisme Polymorphisme
Département Informatiquevoid main() {
Homme HD(« Henri », « Dupont »);
Femme FD(« Francine », « Durand »);
Humain *pH;
cout<<HD.DonneGenre();
cout<<FD.DonneGenre();
pH = &HD;
cout<<pH->DonneGenre();
pH->Affiche();
pH = &FD;
cout<<pH->DonneGenre();
pH->Affiche();
}
Polymorphisme Polymorphisme
Département Informatique
Prochain cours : Notion d'interface
A la semaine prochaine !