'>

le Polymorphisme dans C ++

 Avant d'entrer dans cette section, il est recommandé que vous avez une bonne compréhension des pointeurs et l'héritage de classe. Si l'un des énoncés suivants vous paraître étrange, vous devriez revoir les sections indiquées:

Déclaration:     Expliqué dans:
int a :: b (int c) {}     Classes
a-> b     Structures de données
Classe A: b publique {};     Amitié et l'héritage

Pointeurs vers la classe de base
Une des principales caractéristiques de classes dérivées, c'est que un pointeur vers une classe dérivée est compatible avec le type avec un pointeur vers sa classe de base. Le polymorphisme est l'art de prendre avantage de cette fonctionnalité simple mais puissant et polyvalent, qui apporte les méthodologies orientées objet à son plein potentiel.

Nous allons commencer par réécrire notre programme sur le rectangle et le triangle de la section précédente en tenant compte de cette propriété de compatibilité pointeur:

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35


  

 // pointers to base class #include <iostream> using namespace std; class CPolygon { protected : int width, height; public : void set_values ( int a, int b) { width=a; height=b; } }; class CRectangle: public CPolygon { public : int area () { return (width * height); } }; class CTriangle: public CPolygon { public : int area () { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = &rect; CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << rect.area() << endl; cout << trgl.area() << endl; return 0; }

  

  20
  10



En fonction principale, nous créons deux pointeurs qui pointent vers des objets de CPolygon de classe (ppoly1 et ppoly2). Ensuite, nous attribuons des références à rect et TRGL à ces pointeurs, et parce que les deux sont des objets de classes dérivées de CPolygon, les deux sont des opérations de cession valide.

La seule limitation dans l'utilisation * ppoly1 et ppoly2 * au lieu de rect et TRGL est que les deux ppoly1 * et * ppoly2 sont de type CPolygon * et par conséquent, nous ne pouvons utiliser ces pointeurs pour désigner les membres qui CRectangle et CTriangle héritent de CPolygon. Pour cette raison, lorsque nous appelons les membres de la zone () à la fin du programme, nous avons dû utiliser directement les objets rect et TRGL la place des pointeurs * ppoly1 et * ppoly2.

Pour utiliser area () avec les pointeurs vers CPolygon de classe, ce membre aurait également dû être déclarée dans le CPolygon de classe, et pas seulement dans ses classes dérivées, mais le problème est que CRectangle et CTriangle mettre en œuvre les différentes versions de région, donc nous ne peut pas mettre en œuvre dans la classe de base. C'est alors que les membres virtuels deviennent pratique:

Membres virtuels
Un membre d'une classe qui peut être redéfinie dans ses classes dérivées est connu comme un membre virtuel. Pour déclarer un membre d'une classe virtuelle, nous devons précéder sa déclaration avec le mot-clé virtuelle:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41


  

 // virtual members #include <iostream> using namespace std; class CPolygon { protected : int width, height; public : void set_values ( int a, int b) { width=a; height=b; } virtual int area () { return (0); } }; class CRectangle: public CPolygon { public : int area () { return (width * height); } }; class CTriangle: public CPolygon { public : int area () { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon poly; CPolygon * ppoly1 = &rect; CPolygon * ppoly2 = &trgl; CPolygon * ppoly3 = &poly; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly3->set_values (4,5); cout << ppoly1->area() << endl; cout << ppoly2->area() << endl; cout << ppoly3->area() << endl; return 0; }

  

  20
  10
  0



Maintenant, les trois classes (CPolygon, CRectangle et CTriangle) ont tous les mêmes membres: largeur, hauteur, set_values ​​() et la région ().

La zone de fonction membre () a été déclarée comme virtuelle dans la classe de base, car il est tard redéfini dans chaque classe dérivée. Vous pouvez vérifier si vous souhaitez que si vous supprimez ce mot-clé virtuelle à partir de la déclaration de zone () dans CPolygon, puis vous exécutez le programme le résultat sera 0 pour les trois polygones au lieu de 20, 10 et 0. C'est parce que au lieu d'appeler la zone correspondante () la fonction de chaque objet (CRectangle :: Zone (), CTriangle :: Zone () et CPolygon :: area (), respectivement), CPolygon :: Zone () sera appelée à tous les cas, étant donné que les appels se font via un pointeur dont le type est CPolygon *.

Par conséquent, ce que le mot-clé virtuelle fait est de permettre à un membre d'une classe dérivée avec le même nom que celui de la classe de base d'être appelé de manière appropriée à partir d'un pointeur, et plus précisément lorsque le type du pointeur est un pointeur vers la classe de base mais est pointé vers un objet de la classe dérivée, comme dans l'exemple ci-dessus.

Une classe qui déclare ou hérite d'une fonction virtuelle est appelée une classe polymorphe.

Notez que malgré sa virtualité, nous avons également été en mesure de déclarer un objet de type CPolygon et d'appeler sa propre fonction de zone (), qui retourne toujours 0.

Classes de base abstraites
Résumé des classes de base sont quelque chose de très similaire à notre classe CPolygon de notre exemple précédent. La seule différence est que, dans notre exemple précédent, nous avons défini une fonction zone valide () avec un minimum de fonctionnalités pour les objets qui étaient de CPolygon de classe (comme l'objet poly), alors que dans une des classes de base abstraites nous pouvions laisser membre que de la zone () fonctionner sans la mise en œuvre du tout. Ceci est fait en ajoutant = 0 (égale à zéro) à la déclaration de la fonction.

Une classe d'CPolygon de base abstraite pourrait ressembler à ceci:

 1
 2
 3
 4
 5
 6
 7
 8
 9


  

 // abstract class CPolygon class CPolygon { protected : int width, height; public : void set_values ( int a, int b) { width=a; height=b; } virtual int area () =0; };



Remarquez comment nous annexés = 0 à la zone d'int virtuel () au lieu de spécifier une implémentation de la fonction. Ce type de fonction est appelée une fonction virtuelle pure, et toutes les classes qui contiennent au moins une fonction virtuelle pure sont des classes de base abstraites.

La principale différence entre une classe de base abstraite et une classe polymorphe régulier est que, parce que dans les classes de base abstraites au moins un de ses membres manque de mise en œuvre, nous ne pouvons pas créer des instances (objets) de celui-ci.

Mais une classe qui ne peut pas instancier des objets n'est pas totalement inutile. Nous pouvons créer des pointeurs vers elle et profiter de toutes ses capacités polymorphes. Par conséquent, une telle déclaration:



  

 CPolygon poly;



ne serait pas valide pour la classe de base abstraite que nous avons vient de déclarer, parce essaie d'instancier un objet. Néanmoins, les conseils suivants:

 1
 2


  

 CPolygon * ppoly1; CPolygon * ppoly2;



serait parfaitement valable.

Il en est ainsi aussi longtemps que CPolygon comprend une fonction virtuelle pure et donc c'est une classe de base abstraite. Cependant, des pointeurs vers cette classe de base abstraite peut être utilisé pour pointer vers des objets de classes dérivées.

Ici vous avez un exemple complet:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36


  

 // abstract base class #include <iostream> using namespace std; class CPolygon { protected : int width, height; public : void set_values ( int a, int b) { width=a; height=b; } virtual int area ( void ) =0; }; class CRectangle: public CPolygon { public : int area ( void ) { return (width * height); } }; class CTriangle: public CPolygon { public : int area ( void ) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = &rect; CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << ppoly1->area() << endl; cout << ppoly2->area() << endl; return 0; }

  

  20
  10



Si vous passez en revue le programme, vous remarquerez que nous nous référons à des objets de classes différentes mais liées en utilisant un unique type de pointeur (CPolygon *). Cela peut être extrêmement utile. Par exemple, maintenant, nous pouvons créer une fonction membre de l'abstrait CPolygon de classe de base qui est capable d'imprimer sur l'écran le résultat de la zone () fonctionner même si CPolygon lui-même n'a pas d'implémentation de cette fonction:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39


  

 // pure virtual members can be called // from the abstract base class #include <iostream> using namespace std; class CPolygon { protected : int width, height; public : void set_values ( int a, int b) { width=a; height=b; } virtual int area ( void ) =0; void printarea ( void ) { cout << this ->area() << endl; } }; class CRectangle: public CPolygon { public : int area ( void ) { return (width * height); } }; class CTriangle: public CPolygon { public : int area ( void ) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = &rect; CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly1->printarea(); ppoly2->printarea(); return 0; }

  

  20
  10



Les membres virtuels et des classes abstraites subvention C + + les caractéristiques polymorphes qui rendent la programmation orientée objet d'un tel instrument utile dans les grands projets. Bien sûr, nous avons vu des usages très simples de ces fonctionnalités, mais ces caractéristiques peuvent être appliquées à des tableaux d'objets ou d'objets alloués dynamiquement.

Finissons avec le même exemple, mais cette fois avec des objets qui sont allouées dynamiquement:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38


  

 // dynamic allocation and polymorphism #include <iostream> using namespace std; class CPolygon { protected : int width, height; public : void set_values ( int a, int b) { width=a; height=b; } virtual int area ( void ) =0; void printarea ( void ) { cout << this ->area() << endl; } }; class CRectangle: public CPolygon { public : int area ( void ) { return (width * height); } }; class CTriangle: public CPolygon { public : int area ( void ) { return (width * height / 2); } }; int main () { CPolygon * ppoly1 = new CRectangle; CPolygon * ppoly2 = new CTriangle; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly1->printarea(); ppoly2->printarea(); delete ppoly1; delete ppoly2; return 0; }

  

  20
  10



Remarquez que les pointeurs pPoly:

 1
 2


  

 CPolygon * ppoly1 = new CRectangle; CPolygon * ppoly2 = new CTriangle;



sont déclarés étant de type pointeur sur CPolygon mais les objets alloués dynamiquement ont été déclarés ayant le type de classe dérivée directement.
Publié par Drupal Study