Polymorphisme

Le polymorphisme est l'opportunité pour différentes classes d'objets, liées entre elles par un héritage, de répondre de différentes façons lors de l'appel à la même fonction. Il aide à créer un mécanisme universel décrivant le comportement non seulement de la classe de base, mais aussi des classes descendantes.

Continuons à développer une classe de base CShape, et définissons une fonction membre GetArea(), conçue pour calculer l'aire d'une forme. Dans toutes les classes descendantes, produites par héritage depuis la classe de base, nous redéfinissons cette fonction selon les règles de calcul de l'aire d'une forme particulière.

Pour un carré (classe CSquare), l'aire est calculée grâce à ses côtés, pour un cercle (class CCircle), l'aire est exprimée grâce à son rayon, etc. Nous pouvons créer un tableau pour stocker des objets de type CShape, dans lesquels à la fois des objets de la classe de base et ceux des classes descendantes peuvent être stockés. Par la suite, nous pourrons appeler la même fonction pour tous les éléments du tableau.

Exemple :

//--- Classes de base
class CShape
  {
protected
   int            m_type;                // Type de la forme
   int            m_xpos;                // X - coordonnées du point de base
   int            m_ypos;                // Y - coordonnées du point de base
public:
   void           CShape(){m_type=0;};   // constructeur, type=0
   int            GetType(){return(m_type);};// retourne le type de la forme
virtual
   double         GetArea(){return (0); }// retourne l'aire de la forme
  };

Maintenant, toutes les classes dérivées ont une fonction membre getArea(), qui retourne une valeur zéro. L'implémentation de cette fonction variera dans chaque descendant.

//--- La classe dérivée Circle
class CCircle : public CShape            // Après le symbole ':' se trouve la classe de base
  {                                      // à partir de laquelle l'héritage est effectué
private:
   double         m_radius;              // rayon du cercle
 
public:
   void           CCircle(){m_type=1;};  // constructeur, type=1 
   void           SetRadius(double r){m_radius=r;};
   virtual double GetArea(){return (3.14*m_radius*m_radius);}// aire du cercle
  };

Pour la classe Carré, la déclaration est identique :

//--- La classe dérivée Carré
class CSquare : public CShape            // Après le symbole ':' se trouve la classe de base
  {                                      // à partir de laquelle l'héritage est effectué
private:
   double          m_square_side;        // côté du carré
 
public:
   void            CSquare(){m_type=2;}; // constructeur, type=1
   void            SetSide(double s){m_square_side=s;};
   virtual double  GetArea(){return (m_square_side*m_square_side);}// aire du carré
  };

Pour calculer l'aire du carré et du cercle, nous avons besoin des valeurs correspondantes de m_radius et de m_square_side, nous avons donc ajouté les fonctions SetRadius() et SetSide() dans la déclaration de la classe correspondante.

Il est assumé que des objets de types différents (CCircle et CSquare) dérivés du même type de base CShape, sont utilisés dans notre programme. Le polymorphisme permet de créer un tableau d'objets de la classe de base CShape, mais lors de la déclaration de ce tableau, ces objets ne sont pas encore connus et leurs types sont indéfinis.

La décision sur quel type d'objet sera contenu dans chaque élément du tableau sera prise directement pendant l'exécution du programme. Ceci implique la création dynamique des objets des classes correspondantes, et donc la nécéssité d'utiliser d'utiliser des pointeurs d'objet au lieu des objets directement.

L'opérateur new est utilisé pour créer dynamiquement les objets. Chaque objet doit ensuite être supprimé individuellement et explicitement en utilisant l'opérateur delete. Nous déclarerons donc un tableau de pointeurs de type CShape, et nous créerons un objet du bon type pour chaque élément (new Class_Name), comme montré dans l'exemple du script suivant :

//+------------------------------------------------------------------+
//| Fonction de lancement du programme                               |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Déclare un tableau de pointeurs d'objets du type de base
   CShape *shapes[5];   // Un tableau de pointeurs d'objets CShape
 
//--- Remplissage ici du tableau avec les objets dérivés
//--- Déclaration d'un pointeur d'objet de type CCircle
   CCircle *circle=new CCircle();
//--- Définit les propriétés de l'objet du pointeur du cercle
   circle.SetRadius(2.5);
//--- Place la valeur du pointeur dans shapes[0]
   shapes[0]=circle;
 
//--- Crée un autre objet CCircle et écrit son pointeur dans shapes[1]
   circle=new CCircle();
   shapes[1]=circle;
   circle.SetRadius(5);
 
//--- Nous "oublions" ici intentionnellement de définir une valeur pour shapes[2]
//circle=new CCircle();
//circle.SetRadius(10);
//shapes[2]=circle;
 
//--- Affecte NULL pour l'élément qui n'est pas utilisé
   shapes[2]=NULL;
 
//--- Crée un objet CSquare et écrit son pointeur dans shapes[3]
   CSquare *square=new CSquare();
   square.SetSide(5);
   shapes[3]=square;
 
//--- Crée un objet CSquare et écrit son pointeur dans shapes[4]
   square=new CSquare();
   square.SetSide(10);
   shapes[4]=square;
 
//--- Nous avons un tableau de pointeurs, récupérons sa taille
   int total=ArraySize(shapes);
//--- Passe en boucle sur tous les pointeurs du tableau
   for(int i=0; i<5;i++)
     {
      //--- Si le pointeur à l'indice spécifié est valide
      if(CheckPointer(shapes[i])!=POINTER_INVALID)
        {
         //--- Ecrit le type et l'aire de la forme
         PrintFormat("L'objet de type %d à une surface carrée de %G",
               shapes[i].GetType(),
               shapes[i].GetArea());
        }
      //--- Si le pointeur est du type POINTER_INVALID
      else
        {
         //--- Notifie d'une erreur
         PrintFormat("L'objet shapes[%d] n'a pas été initialisé ! Son pointeur est %s",
                     i,EnumToString(CheckPointer(shapes[i])));
        }
     }
 
//--- Nous devons effacer tous les objets créés dynamiquement
   for(int i=0;i<total;i++)
     {
      //--- Nous ne pouvons supprimer que les objets avec un pointeur de type POINTER_DYNAMIC
      if(CheckPointer(shapes[i])==POINTER_DYNAMIC)
        {
         //--- Notifie de la suppression
         PrintFormat("Suppression de shapes[%d]",i);
         //--- Supprime un objet par son pointeur
         delete shapes[i];
        }
     }
  }

Veuillez noter que lors de la suppression d'un objet avec l'opérateur delete, le type de son pointeur doit être vérifié. Seuls les objets avec un pointeur de type POINTER_DYNAMIC peuvent être supprimés avec delete. Pour les pointeurs d'autres types, une erreur sera retournée.

Mais outre la redéfinition des fonctions pendant l'héritage, le polymorphisme inclut également l'implémentation d'une même fonction avec différents ensembles de paramètres dans une classe. Cela signifie que la classe peut avoir plusieurs fonctions avec le même nom mais avec un type différent et/ou un ensemble de paramères différent. Dans ce cas, le polymorphisme est implémenté via la surcharge de fonction.

Voir aussi

Bibliothèque Standard