Vous êtes sur la page 1sur 48

Programmation Oriente Objet en C++

12me Partie: Polymorphisme

Fabio Hernandez
Fabio.Hernandez@in2p3.fr

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 = &rect;
perimeter = rectPtr->perimeter();
// Rectangle::perimeter() is called
polyPtr = &rect;
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 = &rect;
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