Programmation Oriente Objet en C++
12me Partie: Polymorphisme
Fabio Hernandez
[email protected]
Vue d'Ensemble
}
}
}
}
}
}
}
}
}
}
}
}
}
}
Notions de base
Types, variables, oprateurs
Contrle d'excution
Fonctions
Mmoire dynamique
Qualit du logiciel
Evolution du modle objet
Objets et classes
Fonctions membres
Classes gnriques
Hritage
Polymorphisme
Hritage multiple
Entre/sortie
POO en C++: Polymorphisme
401
1997-2003 Fabio HERNANDEZ
Table des Matires
}
}
}
}
}
}
}
}
Motivation
Affectation polymorphe
Structures de donnes polymorphes
Liaison statique
Liaison dynamique
Mthodes abstraites
Classes abstraites
Rsum
POO en C++: Polymorphisme
402
1997-2003 Fabio HERNANDEZ
Motivation
}
Le mcanisme d'hritage permet aux sous-classes d'utiliser
l'implmentation des mthodes de la classe de base
Nous allons tudier un mcanisme troitement li l'hritage
appel polymorphisme
Polymorphisme signifie la possibilit d un objet de prendre
plusieurs formes
Dans le contexte du modle objet cela signifie qu'une "entit"
du langage peut tre attache en temps d'excution des
objets de classes diffrentes
Dans le cas de C++, cette "entit" ne peut tre qu'un pointeur
ou une rfrence
POO en C++: Polymorphisme
403
1997-2003 Fabio HERNANDEZ
Motivation (suite)
}
Ce mcanisme permet de manipuler d'une faon uniforme un
ensemble d'objets appartenant une mme hirarchie de
classes
POO en C++: Polymorphisme
404
1997-2003 Fabio HERNANDEZ
Contrle d'Avancement
9 Motivation
0Affectation polymorphe
} Structures de donnes polymorphes
} Liaison statique
} Liaison dynamique
} Mthodes abstraites
} Classes abstraites
} Rsum
POO en C++: Polymorphisme
405
1997-2003 Fabio HERNANDEZ
Affectation Polymorphe
Figure
ClosedFigure
OpenFigure
Segment
Polyline
Polygon
Rectangle
Triangle
Ellipse
...
Circle
Square
POO en C++: Polymorphisme
406
1997-2003 Fabio HERNANDEZ
Affectation Polymorphe (suite)
}
Si nous dclarons les objets
Polygon aPolygon;
Triangle aTriangle;
Square aSquare;
Nous pouvons dclarer le pointeur
Polygon* polygonPtr;
Et les affectations suivantes sont valides
polygonPtr = &aTriangle; // aTriangle is-a Polygon
polygonPtr = &aSquare;
// aSquare is-a Polygon
polygonPtr = new Rectangle; // a Rectangle is-a Polygon
Le mcanisme d'hritage nous permet de traiter une instance
de la classe Triangle ou Square comme une instance de
Polygon
POO en C++: Polymorphisme
407
1997-2003 Fabio HERNANDEZ
Affectation Polymorphe (suite)
}
Notez qu'il n'y a aucune transformation des objets aTriangle
et aSquare
une fois cre un objet ne change pas son type
Les pointeurs et rfrences peuvent tre "attachs" des
objets de types diffrents descendants d'un anctre commun
Pour le passage de paramtres nous pouvons utiliser le mme
principe
soit la fonction
void inspect(const Polygon& aPolygon)
{
// Do something with the parameter object
}
POO en C++: Polymorphisme
408
1997-2003 Fabio HERNANDEZ
Affectation Polymorphe (suite)
}
Nous pouvons appeler cette fonction avec comme argument un
objet descendant de Polygon
Square aSquare;
Triangle aTriangle;
inspect(aSquare);
inspect(aTriangle);
Circle aCircle;
inspect(aCircle);
POO en C++: Polymorphisme
// OK: aSquare is a Polygon
// OK: aTriangle is a Polygon
// COMPILATION ERROR: aCircle is
// not a kind of Polygon
409
1997-2003 Fabio HERNANDEZ
Contrle d'Avancement
9 Motivation
9 Affectation polymorphe
0Structures de donnes polymorphes
} Liaison statique
} Liaison dynamique
} Mthodes abstraites
} Classes abstraites
} Rsum
POO en C++: Polymorphisme
410
1997-2003 Fabio HERNANDEZ
Structures de Donnes Polymorphes
}
Soit le tableau
Polygon* polygonArray[4];
polygonArray[0] = new Rectangle;
polygonArray[1] = new Square;
polygonArray[2] = new Triangle;
polygonArray[3] = new Polygon;
Nous pouvons le visualiser comme
0
1
2
3
polygonArray
POO en C++: Polymorphisme
411
1997-2003 Fabio HERNANDEZ
Structures de Donnes Polymorphes (suite)
}
Une structure polymorphe est une structure qui contient des
objets de types diffrents descendants d'une classe commune
Tous les conteneurs que nous avons tudis peuvent tre des
structures polymorphes (List, Queue, Set, Bag, Stack, ...)
L'intrt des conteneurs polymorphes c'est qu'ils offrent la
possibilit de traiter d'une faon uniforme tous les objets
contenus
Supposons par exemple que nous voulons calculer la somme des
primtres des polygones contenus dans notre tableau
Une faon naturelle serait de faire une boucle pour parcourir
chacune des positions du tableau en calculant le primtre du
polygone correspondant et d'en faire l'addition
POO en C++: Polymorphisme
412
1997-2003 Fabio HERNANDEZ
Structures de Donnes Polymorphes (suite)
}
Calcul du primtre (suite)
float total = 0.0;
for (int pos=0; pos < MaxPositions; pos++) {
total += Perimeter of polygonArray[pos];
}
Quelle mthode faudrait-il appeler sur l'objet point par
polygonArray[pos] pour obtenir son primtre?
Regardons la dfinition de la classe Polygon
POO en C++: Polymorphisme
413
1997-2003 Fabio HERNANDEZ
Classe Polygon
#include "Point.h"
#include "List.h"
class Polygon {
public:
// Constructors/Destructor
Polygon();
~Polygon();
// Modifiers
void translate(float horizontal, float vertical);
void rotate(float angle);
...
POO en C++: Polymorphisme
414
1997-2003 Fabio HERNANDEZ
Classe Polygon (suite)
// Selectors
float perimeter() const;
float area() const;
...
private:
// Data members
List<Point> vertexList_;
};
POO en C++: Polymorphisme
415
1997-2003 Fabio HERNANDEZ
Classe Polygon (suite)
}
L'implmentation de la fonction membre Polygon::perimeter
pourrait tre
float Polygon::perimeter() const
{
int numVertices = vertexList_.length();
float result = 0.0;
for (int i=1; i < numVertices; i++) {
const Point& previous = vertexList_.itemAt(i-1);
const Point& current = vertexList_.itemAt(i);
result += current.distanceTo(previous);
}
const Point& first = vertexList_.first();
const Point& last = vertexList_.last();
return result + first.distanceTo(last);
}
POO en C++: Polymorphisme
416
1997-2003 Fabio HERNANDEZ
Classe Rectangle
class Rectangle: public Polygon {
public:
// Constructors/Destructor
Rectangle(const Point& origin,
float side1,
float side2);
~Rectangle();
side2
#include "Point.h"
#include "Polygon.h"
side1
origin
// Modifiers
...
POO en C++: Polymorphisme
417
1997-2003 Fabio HERNANDEZ
Classe Rectangle (suite)
// Selectors
float perimeter() const;
float area() const;
float diagonal() const;
...
private:
// Data members
float side1_;
float side2_;
Point origin_;
};
POO en C++: Polymorphisme
418
Fonctions et
donnes membres
spcifiques la
classe Rectangle
1997-2003 Fabio HERNANDEZ
Classe Rectangle (suite)
}
L'implmentation de la mthode Rectangle::perimeter est
plus simple que Polygon::perimeter
float Rectangle::perimeter() const
{
return 2*(side1_ + side2_);
}
Rectangle est donc une spcialisation de la classe Polygon et
Rectangle::perimeter est une redfinition de
Polygon::perimeter
De faon similaire pour la mthode Rectangle::area
POO en C++: Polymorphisme
419
1997-2003 Fabio HERNANDEZ
Contrle d'Avancement
9 Motivation
9 Affectation polymorphe
9 Structures de donnes polymorphes
0Liaison statique
} Liaison dynamique
} Mthodes abstraites
} Classes abstraites
} Rsum
POO en C++: Polymorphisme
420
1997-2003 Fabio HERNANDEZ
Liaison Statique
}
}
Static Binding
La liaison est le mcanisme utilis par le compilateur pour
dterminer quelle fonction membre appeler sur un objet qui
appartient une hirarchie de classes lorsqu'il y a redfinition
de mthodes
Exemple
Rectangle rect(Point(0.0, 1.0), 10.0, 15.0);
Polygon poly;
float perimeter;
perimeter = rect.perimeter();
// Rectangle::perimeter() is called
perimeter = poly.perimeter();
// Polygon::perimeter() is called
POO en C++: Polymorphisme
421
1997-2003 Fabio HERNANDEZ
Liaison Statique (suite)
}
Exemple (suite)
Polygon* polyPtr = &poly;
perimeter = polyPtr->perimeter();
// Polygon::perimeter() is called
Rectangle* rectPtr = ▭
perimeter = rectPtr->perimeter();
// Rectangle::perimeter() is called
polyPtr = ▭
perimeter = polyPtr->perimeter();
// Rectangle::perimeter() or Polygon::perimeter()?
POO en C++: Polymorphisme
422
1997-2003 Fabio HERNANDEZ
Liaison Statique (suite)
}
Exemple (suite)
La fonction membre appele est Polygon::perimeter()
Le principe de liaison statique tablit que le type de l'objet sur
lequel la mthode est applique dtermine statiquement la
mthode appele
Dans l'exemple prcdent, le pointeur polyPtr pointe vers un
objet de la classe Polygon; en consquence, l'instruction
polyPtr->perimeter()
se traduit par une invocation la mthode
Polygon::perimeter()
POO en C++: Polymorphisme
423
1997-2003 Fabio HERNANDEZ
Liaison Statique (suite)
}
De faon similaire l'instruction
float diagonal = polyPtr->diagonal(); // ERROR
est marque par une erreur de compilation: polyPtr est dfini
comme un pointeur Polygon et la mthode
Polygon::diagonal n'est pas dfinie
Par contre avec les instructions
Rectangle* rectPtr = ▭
float diagonal = rectPtr->diagonal(); // OK
on obtient le rsultat souhait
Ce principe de liaison (binding) est appel statique parce que le
choix de la mthode appeler est fait en temps de compilation
POO en C++: Polymorphisme
424
1997-2003 Fabio HERNANDEZ
Contrle d'Avancement
9 Motivation
9 Affectation polymorphe
9 Structures de donnes polymorphes
9 Liaison statique
0Liaison dynamique
} Mthodes abstraites
} Classes abstraites
} Rsum
POO en C++: Polymorphisme
425
1997-2003 Fabio HERNANDEZ
Liaison Dynamique
}
Dans l'exemple prcdant du calcul du primtre
float total = 0.0;
for (int pos=0; pos < MaxPositions; pos++)
total += polygonArray[pos]->perimeter();
la mthode qui sera appele pour chaque objet du tableau est
Polygon::perimeter()
}
Supposons que nous disposons d'une mthode pour connatre en
temps d'excution la classe d'un objet
On pourrait crire par exemple
if (type of polygonArray[pos] == Rectangle)
// WARNING: this is pseudo-code
pour dterminer si un objet est de type Rectangle
POO en C++: Polymorphisme
426
1997-2003 Fabio HERNANDEZ
Liaison Dynamique (suite)
}
Une faon de rsoudre ce problme et d'appeler la bonne
mthode serait
float total = 0.0;
for (int pos=0; pos < MaxPositions; pos++) {
// WARNING: this is pseudo-code
if (type of polygonArray[pos] == Rectangle)
total +=
((Rectangle*)polygonArray[pos])->perimeter();
else if (type of polygonArray[pos] == Triangle)
total +=
((Triangle*)polygonArray[pos])->perimeter();
....
}
POO en C++: Polymorphisme
427
1997-2003 Fabio HERNANDEZ
Liaison Dynamique (suite)
}
L'inconvnient de cette technique c'est qu'elle rend difficile
les modifications
Si une nouvelle sous-classe de Polygon est rajoute ou une sous-classe
existante est supprime de la hirarchie, cette boucle doit tre
modifie
Le modle objet propose une technique pour rsoudre ce
problme appele "Liaison Dynamique" (dynamic binding)
Par opposition au principe de liaison statique, avec la liaison
dynamique le compilateur ne peut dcider en temps de
compilation quelle mthode appeler. Cette dcision est prise en
temps d'excution, par rapport la classe de l'objet en
question (Rectangle, Triangle, Polygon, ...)
POO en C++: Polymorphisme
428
1997-2003 Fabio HERNANDEZ
Liaison Dynamique (suite)
}
}
}
Contrairement d'autres langages OO, C++ utilise par dfaut la
liaison statique
Le programmeur est donc responsable d'informer le
compilateur que pour une ou plusieurs mthodes d'une classe il
souhaite utiliser la liaison dynamique
Le mot cl du langage pour exprimer ce concept est virtual
Nous devons en consquence modifier l'interface de notre
classe Polygon pour indiquer que la fonction membre
Polygon::perimeter sera virtual
POO en C++: Polymorphisme
429
1997-2003 Fabio HERNANDEZ
Classe Polygon
class Polygon {
public:
// Constructors/Destructor
...
// Modifiers
...
// Selectors
virtual float perimeter() const;
virtual float area() const;
...
Dfinition des
fonctions membres
Perimeter et area
comme ayant liaison
dynamique
private:
// Data members
...
};
POO en C++: Polymorphisme
430
1997-2003 Fabio HERNANDEZ
Classe Polygon (suite)
}
Notez que l'implmentation des fonctions
Polygon::perimeter et Polygon:: area reste inchange
Les interfaces des sous-classes de Polygon (Rectangle,
Triangle, ...) peuvent rester inchanges. Nanmoins, pour
clart nous allons propager la modification de l'interface de
Polygon toutes ses sous-classes
Regardons le cas de la classe Rectangle
POO en C++: Polymorphisme
431
1997-2003 Fabio HERNANDEZ
Classe Rectangle
class Rectangle: public Polygon {
public:
// Constructors/Destructor
...
// Modifiers
...
// Selectors
virtual float perimeter() const;
virtual float area() const;
virtual float diagonal() const;
...
private:
// Data members
...
};
POO en C++: Polymorphisme
432
Propagation pour
clart de la
dfinition des
fonctions membres
Perimeter et area
comme virtual.
Dfinition de la
mthode diagonal
comme virtual.
Affecte toutes les
sous-classes de
Rectangle
1997-2003 Fabio HERNANDEZ
Calcul du primtre
}
Dans l'exemple du tableau polymorphe
0
1
2
3
l'algorithme
float total = 0.0;
for (int pos=0; pos < MaxPositions; pos++)
total += polygonArray[pos]->perimeter();
appellera Rectangle::perimeter(),
Square::perimeter(), Triangle::perimeter() et
Polygon::perimeter()
POO en C++: Polymorphisme
433
1997-2003 Fabio HERNANDEZ
Destructeur Virtuel
}
Lors de la destruction d'une structure (conteneur) polymorphe
(List, Queue, Stack, Set, Bag, Tree, ...) les objets contenus
seront eux aussi probablement dtruits
Le destructeur de chacun des objets sera utilis
for (int pos=0; pos < MaxPositions; pos++)
delete polygonArray[pos];
Cependant, avec le destructeur nous avons le mme problme
que avec la fonction membre perimeter
}
}
dans ce cas particulier, cause de la liaison statique uniquement le
destructeur Polygon::~Polygon() sera appel
La solution est la mme que pour les autres fonctions membres
Le destructeur de la classe de base doit tre dfini virtual
POO en C++: Polymorphisme
434
1997-2003 Fabio HERNANDEZ
Destructeur Virtuel (suite)
class Polygon {
public:
// Constructors/Destructor
Polygon();
virtual ~Polygon();
// Modifiers
...
Le destructeur doit
tre dclar
virtual.
// Selectors
...
private:
// Data members
...
};
POO en C++: Polymorphisme
435
1997-2003 Fabio HERNANDEZ
Destructeur Virtuel (suite)
}
De faon similaire, pour des raisons de clart nous dclarerons
virtual le destructeur de toutes les sous-classes de Polygon
D'une faon gnrale, on doit dclarer virtual le destructeur
de toute classe contenant au moins une fonction membre
virtuelle
POO en C++: Polymorphisme
436
1997-2003 Fabio HERNANDEZ
Contrle d'Avancement
9 Motivation
9 Affectation polymorphe
9 Structures de donnes polymorphes
9 Liaison statique
9 Liaison dynamique
0Mthodes abstraites
} Classes abstraites
} Rsum
POO en C++: Polymorphisme
437
1997-2003 Fabio HERNANDEZ
Mthodes Abstraites
}
Supposons que nous voulons ajouter une mthode la classe
Polygon pour dterminer si un point (x,y) se trouve
l'intrieur
Le prototype de cette mthode pourrait tre
bool Polygon::isInside(const Point& aPoint) const
Un tel service est facilement implment pour certaines sousclasses de Polygon (Triangle, Rectangle, Square)
L'implmentation est plus difficile pour un polygone gnrique
une implmentation par dfaut n'est pas souhaitable
Il est nanmoins ncessaire que tous les polygones, c'est dire
toutes les sous-classes de Polygon fournissent sa propre
implmentation de ce service
POO en C++: Polymorphisme
438
1997-2003 Fabio HERNANDEZ
Mthodes Abstraites (suite)
}
Une faon de faire c'est d'implmenter le service dans la
classe de base avec une implmentation par dfaut "vide"
bool Polygon::isInside(const Point& aPoint) const
{
cerr << "You must implement this routine" << endl;
return false;
}
Une autre faon de faire c'est de forcer chaque sous-classe
fournir ce service en dclarant la mthode comme abstraite
dans la classe de base
Une mthode abstraite est une mthode pour laquelle la classe
de base ne fournit pas d'implmentation
POO en C++: Polymorphisme
439
1997-2003 Fabio HERNANDEZ
Mthodes Abstraites (suite)
}
Nous pouvons dfinir la mthode Polygon::isInside comme
abstraite
Une mthode abstraite en C++ est aussi appele virtuelle pure
POO en C++: Polymorphisme
440
1997-2003 Fabio HERNANDEZ
Mthodes Abstraites (suite)
class Polygon {
public:
// Constructors/Destructor
...
Mthode Abstraite
ou virtuelle pure
// Modifiers
...
// Selectors
virtual bool isInside(const Point& aPoint) const = 0;
...
private:
// Data members
...
};
POO en C++: Polymorphisme
441
1997-2003 Fabio HERNANDEZ
Contrle d'Avancement
9 Motivation
9 Affectation polymorphe
9 Structures de donnes polymorphes
9 Liaison statique
9 Liaison dynamique
9 Mthodes abstraites
0Classes abstraites
} Rsum
POO en C++: Polymorphisme
442
1997-2003 Fabio HERNANDEZ
Classe Abstraite
}
Une classe ayant au moins une mthode abstraite est appele
classe abstraite
Il est impossible de crer une instance d'une classe abstraite
Cette vrification est effectue par le compilateur
Polygon aPolygon;
// ERROR: Polygon has at least one pure virtual
// member fonction
Les sous-classes (instanciables) de Polygon doivent fournir une
implmentation de cette mthode
POO en C++: Polymorphisme
443
1997-2003 Fabio HERNANDEZ
Classe Abstraite (suite)
class Rectangle: public Polygon {
public:
// Constructors/Destructor
...
// Modifiers
...
// Selectors
virtual bool isInside(const Point& aPoint) const;
...
private:
// Data members
...
};
POO en C++: Polymorphisme
444
1997-2003 Fabio HERNANDEZ
Classe Abstraite (suite)
bool Rectangle::isInside(const Point& aPoint) const
{
if ((origin_.getX() <= aPoint. getX()) &&
(aPoint. getX() <= (origin_. getX() + side1_)) &&
(origin_.getY() <= aPoint. getY()) &&
(aPoint. getY() <= (origin_. getY() + side2_)))
return true;
return false;
}
POO en C++: Polymorphisme
445
1997-2003 Fabio HERNANDEZ
Contrle d'Avancement
9 Motivation
9 Affectation polymorphe
9 Structures de donnes polymorphes
9 Liaison statique
9 Liaison dynamique
9 Mthodes abstraites
9 Classes abstraites
0Rsum
POO en C++: Polymorphisme
446
1997-2003 Fabio HERNANDEZ
Rsum
}
Le polymorphisme permet une rfrence ou un pointeur
d'tre associ en temps d'excution des instances de classes
diffrentes
La liaison dynamique est le mcanisme qui permet de dterminer
en temps d'excution l'utilisation de la redfinition correcte
d'une mthode
Une mthode abstraite ou virtuelle pure est une mthode pour
laquelle la classe de base ne fournit pas d'implmentation
Une classe avec une ou plusieurs mthodes abstraites est elle
aussi abstraite
Il est impossible de crer une instance d'une classe abstraite
POO en C++: Polymorphisme
447
1997-2003 Fabio HERNANDEZ