Partie 12: Polymorphisme — Programmation orientée objet en C++

4,275 views
4,199 views

Published on

Support material for a continued education course "Introduction to object oriented programming in C++".
In French.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,275
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
160
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Partie 12: Polymorphisme — Programmation orientée objet en C++

  1. 1. Programmation Orientée Objet en C++ 12ème Partie: Polymorphisme Fabio Hernandez Fabio.Hernandez@in2p3.fr
  2. 2. Vue dEnsemble Notions de base Types, variables, opérateurs Contrôle dexécution Fonctions Mémoire dynamique Qualité du logiciel Evolution du modèle objet Objets et classes Fonctions membres Classes génériques Héritage Polymorphisme Héritage multiple Entrée/sortiePOO en C++: Polymorphisme 401 © 1997-2003 Fabio HERNANDEZ
  3. 3. Table des Matières Motivation Affectation polymorphe Structures de données polymorphes Liaison statique Liaison dynamique Méthodes abstraites Classes abstraites RésuméPOO en C++: Polymorphisme 402 © 1997-2003 Fabio HERNANDEZ
  4. 4. Motivation Le mécanisme dhéritage permet aux sous-classes dutiliser limplémentation des méthodes de la classe de base Nous allons étudier un mécanisme étroitement lié à lhéritage appelé polymorphisme Polymorphisme signifie la possibilité d ’un objet de prendre plusieurs formes Dans le contexte du modèle objet cela signifie quune "entité" du langage peut être attachée en temps dexécution à des objets de classes différentes Dans le cas de C++, cette "entité" ne peut être quun pointeur ou une référencePOO en C++: Polymorphisme 403 © 1997-2003 Fabio HERNANDEZ
  5. 5. Motivation (suite) Ce mécanisme permet de manipuler dune façon uniforme un ensemble dobjets appartenant à une même hiérarchie de classesPOO en C++: Polymorphisme 404 © 1997-2003 Fabio HERNANDEZ
  6. 6. Contrôle dAvancement Motivation Affectation polymorphe Structures de données polymorphes Liaison statique Liaison dynamique Méthodes abstraites Classes abstraites RésuméPOO en C++: Polymorphisme 405 © 1997-2003 Fabio HERNANDEZ
  7. 7. Affectation Polymorphe Figure OpenFigure ClosedFigure Segment Polyline Polygon Ellipse Triangle Rectangle ... Circle SquarePOO en C++: Polymorphisme 406 © 1997-2003 Fabio HERNANDEZ
  8. 8. Affectation Polymorphe (suite) Si nous déclarons les objets Polygon aPolygon; Triangle aTriangle; Square aSquare; Nous pouvons déclarer 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 mécanisme dhéritage nous permet de traiter une instance de la classe Triangle ou Square comme une instance de PolygonPOO en C++: Polymorphisme 407 © 1997-2003 Fabio HERNANDEZ
  9. 9. Affectation Polymorphe (suite) Notez quil ny a aucune transformation des objets aTriangle et aSquare une fois crée un objet ne change pas son type Les pointeurs et références peuvent être "attachés" à des objets de types différents descendants dun ancêtre commun Pour le passage de paramètres nous pouvons utiliser le même principe soit la fonction void inspect(const Polygon& aPolygon) { // Do something with the parameter object }POO en C++: Polymorphisme 408 © 1997-2003 Fabio HERNANDEZ
  10. 10. Affectation Polymorphe (suite) Nous pouvons appeler cette fonction avec comme argument un objet descendant de Polygon Square aSquare; Triangle aTriangle; inspect(aSquare); // OK: aSquare is a Polygon inspect(aTriangle); // OK: aTriangle is a Polygon Circle aCircle; inspect(aCircle); // COMPILATION ERROR: aCircle is // not a kind of PolygonPOO en C++: Polymorphisme 409 © 1997-2003 Fabio HERNANDEZ
  11. 11. Contrôle dAvancement Motivation Affectation polymorphe Structures de données polymorphes Liaison statique Liaison dynamique Méthodes abstraites Classes abstraites RésuméPOO en C++: Polymorphisme 410 © 1997-2003 Fabio HERNANDEZ
  12. 12. Structures de Données 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 polygonArrayPOO en C++: Polymorphisme 411 © 1997-2003 Fabio HERNANDEZ
  13. 13. Structures de Données Polymorphes (suite) Une structure polymorphe est une structure qui contient des objets de types différents descendants dune classe commune Tous les conteneurs que nous avons étudiés peuvent être des structures polymorphes (List, Queue, Set, Bag, Stack, ...) Lintérêt des conteneurs polymorphes cest quils offrent la possibilité de traiter dune façon uniforme tous les objets contenus Supposons par exemple que nous voulons calculer la somme des périmètres des polygones contenus dans notre tableau Une façon naturelle serait de faire une boucle pour parcourir chacune des positions du tableau en calculant le périmètre du polygone correspondant et den faire ladditionPOO en C++: Polymorphisme 412 © 1997-2003 Fabio HERNANDEZ
  14. 14. Structures de Données Polymorphes (suite) Calcul du périmètre (suite) float total = 0.0; for (int pos=0; pos < MaxPositions; pos++) { total += Perimeter of polygonArray[pos]; } Quelle méthode faudrait-il appeler sur lobjet pointé par polygonArray[pos] pour obtenir son périmètre? Regardons la définition de la classe PolygonPOO en C++: Polymorphisme 413 © 1997-2003 Fabio HERNANDEZ
  15. 15. 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
  16. 16. 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
  17. 17. Classe Polygon (suite) Limplémentation 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
  18. 18. Classe Rectangle #include "Point.h" #include "Polygon.h" class Rectangle: public Polygon { public: side2 // Constructors/Destructor Rectangle(const Point& origin, float side1, float side2); side1 ~Rectangle(); origin // Modifiers ...POO en C++: Polymorphisme 417 © 1997-2003 Fabio HERNANDEZ
  19. 19. Classe Rectangle (suite) // Selectors float perimeter() const; float area() const; float diagonal() const; ... Fonctions et données membres private: spécifiques à la // Data members classe Rectangle float side1_; float side2_; Point origin_; };POO en C++: Polymorphisme 418 © 1997-2003 Fabio HERNANDEZ
  20. 20. Classe Rectangle (suite) Limplémentation de la méthode Rectangle::perimeter est plus simple que Polygon::perimeter float Rectangle::perimeter() const { return 2*(side1_ + side2_); } Rectangle est donc une spécialisation de la classe Polygon et Rectangle::perimeter est une redéfinition de Polygon::perimeter De façon similaire pour la méthode Rectangle::areaPOO en C++: Polymorphisme 419 © 1997-2003 Fabio HERNANDEZ
  21. 21. Contrôle dAvancement Motivation Affectation polymorphe Structures de données polymorphes Liaison statique Liaison dynamique Méthodes abstraites Classes abstraites RésuméPOO en C++: Polymorphisme 420 © 1997-2003 Fabio HERNANDEZ
  22. 22. Liaison Statique Static Binding La liaison est le mécanisme utilisé par le compilateur pour déterminer quelle fonction membre appeler sur un objet qui appartient à une hiérarchie de classes lorsquil y a redéfinition de méthodes 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 calledPOO en C++: Polymorphisme 421 © 1997-2003 Fabio HERNANDEZ
  23. 23. 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
  24. 24. Liaison Statique (suite) Exemple (suite) La fonction membre appelée est Polygon::perimeter() Le principe de liaison statique établit que le type de lobjet sur lequel la méthode est appliquée détermine statiquement la méthode appelée Dans lexemple précédent, le pointeur polyPtr pointe vers un objet de la classe Polygon; en conséquence, linstruction polyPtr->perimeter() se traduit par une invocation à la méthode Polygon::perimeter()POO en C++: Polymorphisme 423 © 1997-2003 Fabio HERNANDEZ
  25. 25. Liaison Statique (suite) De façon similaire linstruction float diagonal = polyPtr->diagonal(); // ERROR est marquée par une erreur de compilation: polyPtr est défini comme un pointeur à Polygon et la méthode Polygon::diagonal nest pas définie Par contre avec les instructions Rectangle* rectPtr = &rect; float diagonal = rectPtr->diagonal(); // OK on obtient le résultat souhaité Ce principe de liaison (binding) est appelé statique parce que le choix de la méthode à appeler est fait en temps de compilationPOO en C++: Polymorphisme 424 © 1997-2003 Fabio HERNANDEZ
  26. 26. Contrôle dAvancement Motivation Affectation polymorphe Structures de données polymorphes Liaison statique Liaison dynamique Méthodes abstraites Classes abstraites RésuméPOO en C++: Polymorphisme 425 © 1997-2003 Fabio HERNANDEZ
  27. 27. Liaison Dynamique Dans lexemple précédant du calcul du périmètre float total = 0.0; for (int pos=0; pos < MaxPositions; pos++) total += polygonArray[pos]->perimeter(); la méthode qui sera appelée pour chaque objet du tableau est Polygon::perimeter() Supposons que nous disposons dune méthode pour connaître en temps dexécution la classe dun objet On pourrait écrire par exemple if (type of polygonArray[pos] == Rectangle) // WARNING: this is pseudo-code pour déterminer si un objet est de type RectanglePOO en C++: Polymorphisme 426 © 1997-2003 Fabio HERNANDEZ
  28. 28. Liaison Dynamique (suite) Une façon de résoudre ce problème et dappeler la bonne méthode 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
  29. 29. Liaison Dynamique (suite) Linconvénient de cette technique cest quelle rend difficile les modifications Si une nouvelle sous-classe de Polygon est rajoutée ou une sous-classe existante est supprimée de la hiérarchie, cette boucle doit être modifiée Le modèle objet propose une technique pour résoudre ce problème appelée "Liaison Dynamique" (dynamic binding) Par opposition au principe de liaison statique, avec la liaison dynamique le compilateur ne peut décider en temps de compilation quelle méthode appeler. Cette décision est prise en temps dexécution, par rapport à la classe de lobjet en question (Rectangle, Triangle, Polygon, ...)POO en C++: Polymorphisme 428 © 1997-2003 Fabio HERNANDEZ
  30. 30. Liaison Dynamique (suite) Contrairement à dautres langages OO, C++ utilise par défaut la liaison statique Le programmeur est donc responsable dinformer le compilateur que pour une ou plusieurs méthodes dune classe il souhaite utiliser la liaison dynamique Le mot clé du langage pour exprimer ce concept est virtual Nous devons en conséquence modifier linterface de notre classe Polygon pour indiquer que la fonction membre Polygon::perimeter sera virtualPOO en C++: Polymorphisme 429 © 1997-2003 Fabio HERNANDEZ
  31. 31. Classe Polygon class Polygon { public: // Constructors/Destructor ... // Modifiers ... // Selectors Définition des virtual float perimeter() const; fonctions membres virtual float area() const; Perimeter et area ... comme ayant liaison dynamique private: // Data members ... };POO en C++: Polymorphisme 430 © 1997-2003 Fabio HERNANDEZ
  32. 32. Classe Polygon (suite) Notez que limplémentation des fonctions Polygon::perimeter et Polygon:: area reste inchangée Les interfaces des sous-classes de Polygon (Rectangle, Triangle, ...) peuvent rester inchangées. Néanmoins, pour clarté nous allons propager la modification de linterface de Polygon à toutes ses sous-classes Regardons le cas de la classe RectanglePOO en C++: Polymorphisme 431 © 1997-2003 Fabio HERNANDEZ
  33. 33. Classe Rectangle class Rectangle: public Polygon { public: Propagation pour // Constructors/Destructor clarté de la ... définition des // Modifiers fonctions membres ... Perimeter et area // Selectors comme virtual. virtual float perimeter() const; virtual float area() const; Définition de la virtual float diagonal() const; méthode diagonal ... comme virtual. private: Affecte toutes les // Data members sous-classes de ... Rectangle };POO en C++: Polymorphisme 432 © 1997-2003 Fabio HERNANDEZ
  34. 34. Calcul du périmètre Dans lexemple du tableau polymorphe 0 1 2 3 lalgorithme 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
  35. 35. Destructeur Virtuel Lors de la destruction dune structure (conteneur) polymorphe (List, Queue, Stack, Set, Bag, Tree, ...) les objets contenus seront eux aussi probablement détruits 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 même problème 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 même que pour les autres fonctions membres Le destructeur de la classe de base doit être défini virtualPOO en C++: Polymorphisme 434 © 1997-2003 Fabio HERNANDEZ
  36. 36. Destructeur Virtuel (suite) class Polygon { public: // Constructors/Destructor Polygon(); virtual ~Polygon(); Le destructeur doit être déclaré // Modifiers virtual. ... // Selectors ... private: // Data members ... };POO en C++: Polymorphisme 435 © 1997-2003 Fabio HERNANDEZ
  37. 37. Destructeur Virtuel (suite) De façon similaire, pour des raisons de clarté nous déclarerons virtual le destructeur de toutes les sous-classes de Polygon Dune façon générale, on doit déclarer virtual le destructeur de toute classe contenant au moins une fonction membre virtuellePOO en C++: Polymorphisme 436 © 1997-2003 Fabio HERNANDEZ
  38. 38. Contrôle dAvancement Motivation Affectation polymorphe Structures de données polymorphes Liaison statique Liaison dynamique Méthodes abstraites Classes abstraites RésuméPOO en C++: Polymorphisme 437 © 1997-2003 Fabio HERNANDEZ
  39. 39. Méthodes Abstraites Supposons que nous voulons ajouter une méthode à la classe Polygon pour déterminer si un point (x,y) se trouve à lintérieur Le prototype de cette méthode pourrait être bool Polygon::isInside(const Point& aPoint) const Un tel service est facilement implémenté pour certaines sous- classes de Polygon (Triangle, Rectangle, Square) Limplémentation est plus difficile pour un polygone générique une implémentation par défaut nest pas souhaitable Il est néanmoins nécessaire que tous les polygones, cest à dire toutes les sous-classes de Polygon fournissent sa propre implémentation de ce servicePOO en C++: Polymorphisme 438 © 1997-2003 Fabio HERNANDEZ
  40. 40. Méthodes Abstraites (suite) Une façon de faire cest dimplémenter le service dans la classe de base avec une implémentation par défaut "vide" bool Polygon::isInside(const Point& aPoint) const { cerr << "You must implement this routine" << endl; return false; } Une autre façon de faire cest de forcer chaque sous-classe à fournir ce service en déclarant la méthode comme abstraite dans la classe de base Une méthode abstraite est une méthode pour laquelle la classe de base ne fournit pas dimplémentationPOO en C++: Polymorphisme 439 © 1997-2003 Fabio HERNANDEZ
  41. 41. Méthodes Abstraites (suite) Nous pouvons définir la méthode Polygon::isInside comme abstraite Une méthode abstraite en C++ est aussi appelée virtuelle purePOO en C++: Polymorphisme 440 © 1997-2003 Fabio HERNANDEZ
  42. 42. Méthodes Abstraites (suite) class Polygon { public: // Constructors/Destructor Méthode 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
  43. 43. Contrôle dAvancement Motivation Affectation polymorphe Structures de données polymorphes Liaison statique Liaison dynamique Méthodes abstraites Classes abstraites RésuméPOO en C++: Polymorphisme 442 © 1997-2003 Fabio HERNANDEZ
  44. 44. Classe Abstraite Une classe ayant au moins une méthode abstraite est appelée classe abstraite Il est impossible de créer une instance dune classe abstraite Cette vérification est effectuée par le compilateur Polygon aPolygon; // ERROR: Polygon has at least one pure virtual // member fonction Les sous-classes (instanciables) de Polygon doivent fournir une implémentation de cette méthodePOO en C++: Polymorphisme 443 © 1997-2003 Fabio HERNANDEZ
  45. 45. 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
  46. 46. 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
  47. 47. Contrôle dAvancement Motivation Affectation polymorphe Structures de données polymorphes Liaison statique Liaison dynamique Méthodes abstraites Classes abstraites RésuméPOO en C++: Polymorphisme 446 © 1997-2003 Fabio HERNANDEZ
  48. 48. Résumé Le polymorphisme permet à une référence ou à un pointeur dêtre associé en temps dexécution à des instances de classes différentes La liaison dynamique est le mécanisme qui permet de déterminer en temps dexécution lutilisation de la redéfinition correcte dune méthode Une méthode abstraite ou virtuelle pure est une méthode pour laquelle la classe de base ne fournit pas dimplémentation Une classe avec une ou plusieurs méthodes abstraites est elle aussi abstraite Il est impossible de créer une instance dune classe abstraitePOO en C++: Polymorphisme 447 © 1997-2003 Fabio HERNANDEZ

×