English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Utilisation des pointeurs d'objet dans MQL5

Utilisation des pointeurs d'objet dans MQL5

MetaTrader 5Exemples | 22 décembre 2021, 16:17
117 0
MetaQuotes
MetaQuotes

Introduction

Dans MQL5, vous pouvez créer votre propre classe pour une utilisation ultérieure des variables de type classe dans votre code. Comme nous le savons déjà dans l'article L'ordre de création et de destruction des objets dans MQL5, les structures et les classes peuvent être créées de deux manières - automatiquement et dynamiquement.

Pour créer un objet automatiquement, déclarez simplement une variable de type classe - le système la créera et l'initialisera automatiquement. Pour créer un objet dynamiquement, il est nécessaire d'appliquer explicitement l'opérateur new au pointeur d'objet.

Cependant, quelle est la différence entre les objets créés automatiquement et dynamiquement, et quand a-t-on nécessairement besoin de l'utilisation du pointeur d'objet, et quand suffit-il de créer les objets automatiquement ? Ce sujet constitue l'objet de cet article. Tout d'abord, discutons de certains pièges possibles lorsque vous travaillez avec des objets et examinons les moyens de les résoudre.

Une erreur critique dans l'accès au pointeur non valide

La première chose que vous devez retenir lors de l'utilisation des pointeurs d'objet - c'est l'initialisation obligatoire de l'objet avant son utilisation. Lorsque vous accédez à un pointeur non valide, le travail du programme MQL se termine par une erreur critique et le programme est donc supprimé. A titre d'exemple, considérons un simple Expert Advisor, avec la classe CHello, déclarée ici. Le pointeur vers l'instance de classe est déclaré au niveau global.

//+------------------------------------------------------------------+
//|                                             GetCriticalError.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   Print(pstatus.GetMessage());
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

La variable pstatus est le pointeur d'objet, mais nous avons intentionnellement « oublié » de créer l'objet lui-même à l'aide de l'opérateur new. Si vous essayez de lancer cet Expert Advisor sur le graphique EURUSD, vous verrez le résultat naturel - l'Expert Advisor a été immédiatement déchargé au stade de l'exécution de la fonction OnInit(). Les messages qui apparaissent dans le Journal des Experts sont les suivants :

14:46:17 Expert GetCriticalError (EURUSD, H1) chargé avec succès
14:46:18 Échec de l'initialisation de GetCriticalError (EURUSD, H1)
14:46:18 Expert GetCriticalError (EURUSD, H1) supprimé

Cet exemple est très simple, la détection d'erreurs est facile. Cependant, si votre programme MQL5 contient des centaines voire des milliers de lignes de code, la détection de ces erreurs peut être très compliquée. C'est important, en particulier pour les cas de situations d'urgence, les conditions dans le comportement du programme dépendent de facteurs imprévisibles - par exemple, de la structure particulière du marché.

Vérification du pointeur avant son utilisation

Était-il possible d'éviter l'arrêt critique du programme ? Oui bien sûr ! Il suffit d'insérer la vérification du pointeur de l'objet avant son utilisation. Modifions cet exemple en ajoutant la fonction PrintStatus :

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void PrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }

Maintenant cette fonction appelle la méthode GetMessage(), le pointeur de l'objet de type CHello est transmis à la fonction. D’abord, il vérifie le pointeur à l'aide de la fonction CheckPointer(). Ajoutons un paramètre externe et enregistrons le code d'un Expert Advisor dans le fichier GetCriticalError_OnDemand.mq5.

//+------------------------------------------------------------------+
//|                                    GetCriticalError_OnDemand.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void PrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      PrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

Nous pouvons maintenant lancer l'Expert Advisor de deux manières :

  1. Avec une erreur critique (GetStop = true)
  2. Sans erreur, mais avec le message sur le pointeur invalide (GetStop = false)

Par défaut, l'exécution d'Expert Advisor est réussie et le message suivant apparaît dans le Journal « Experts » :

GetCriticalError_OnDemand (EURUSD, H1) 15:01:57 PrintStatus la variable 'objet' n'est pas initialisée !
GetCriticalError_OnDemand (EURUSD, H1) 15:01:57 La fonction OnInit OnInit () est terminée

Ainsi, la vérification du pointeur avant son utilisation permet d'éviter les erreurs critiques.

Vérifier toujours l'exactitude du pointeur avant son utilisation dans la fonction

Transmission de l'objet non initialisé par référence

Que se passe-t-il si vous transmis l'objet non initialisé comme paramètre d'entrée de la fonction ? (l'objet lui-même par référence, pas le pointeur d'objet). Les objets complexes, tels que les classes et les structures sont transmis par référence avec une esperluette. Réécrivons du code de GetCriticalError_OnDemand.mq5. Renommons la fonction PrintStatus() et réécrivons son code d'une manière différente.

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }

Maintenant, la différence est que la variable elle-même de ce type est transmise par référence en tant que paramètre d'entrée au lieu du pointeur d'objet de type CClassHello. Enregistrons la nouvelle version d'un Expert Advisor sous le nom GetCriticalError_Unsafe.mq5.

//+------------------------------------------------------------------+
//|                                      GetCriticalError_Unsafe.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      UnsafePrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

On peut voir la différence entre les Expert Advisors GetCriticalError_OnDemand.mq5 et GetCriticalError_Unsafe.mq5 dans la manière de transmettre des paramètres à la fonction. Dans le premier cas, le pointeur d'objet est transmis à la fonction, et pour le second cas, l'objet lui-même est transmis par référence. Dans les deux cas, avant l'utilisation de l'objet et de son pointeur, la fonction vérifie l'exactitude du pointeur.

Cela signifie-t-il que les Expert Advisors fonctionneront de la même manière ? Non, ce n'est pas le cas ! Lançons l'Expert Advisor avec le paramètre GetStop=false, et nous obtiendrons à nouveau une erreur critique. Le fait est que si un objet est transmis par référence, l'erreur critique se produit à l'étape d'un appel de fonction, car l'objet non initialisé est transmis sous forme de paramètre. Vous pouvez le vérifier, si vous lancez le script en mode débogage directement depuis MetaEditor5, à l'aide du bouton F5 .

Pour éviter l'utilisation des points d'arrêt manuels, modifions la fonction en ajoutant le point d'arrêt DebugBreak () à l'intérieur de la fonction.

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }

En mode débogage, l'Expert Advisor sera déchargé avant un appel de la fonction DebugBreak() . Comment écrire le code sécurisé pour le cas où un objet non initialisé est transmis par référence ? La réponse est simple - utiliser la surcharge de fonction.

Si un pointeur d'un objet non initialisé est transmis par référence en tant que paramètre de la fonction, cela conduira à une erreur critique et arrêtera le programme mql5.

Utilisation des fonctions surchargées pour un code sécurisé

Ce serait très gênant si le développeur, qui utilise la bibliothèque externe, était obligé de vérifier l'exactitude des objets d'entrée. Il est préférable d'effectuer toutes les vérifications nécessaires à l'intérieur de la bibliothèque, cela peut être implémenté en utilisant la surcharge de fonction.

Implémentons la méthode UnsafePrintStatus() avec la surcharge de fonction et écrivons deux versions de cette fonction - la première qui utilise le pointeur d'objet passé au lieu de l'objet lui-même, la seconde qui utilise le passage d'objet par référence. Les deux fonctions auront le même nom "PrintStatus", mais cette mise en œuvre ne sera plus potentiellement dangereuse.

//+------------------------------------------------------------------+
//| The safe printing of a message using the CHello object pointer     |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Printing a message using the CHello object, passed by reference  |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello &pobject)
  {
   DebugBreak();
   SafePrintStatus(GetPointer(pobject));
  }

Maintenant, dans le cas où la fonction est appelée par transmission du pointeur d'objet, la vérification de l'exactitude est effectuée et l'erreur critique ne se produit pas. Si vous appelez la fonction surchargée avec la transmission d'un objet par référence, nous obtenons d'abord le pointeur d'objet à l'aide de la fonction GetPointer, puis nous appelons le code sécurisé, qui utilise la transmission d'un objet par le pointeur.

Nous pouvons réécrire une version sécurisée de la fonction qui soit encore plus courte, plutôt que

void SafePrintStatus (CHello & pobject)
  (
   DebugBreak ();
   CHello * p = GetPointer (pobject);
   SafePrintStatus (p);
  )

d’écrire la version compacte :

void SafePrintStatus (CHello & object)
  (
   DebugBreak ();
   SafePrintStatus (GetPointer (object));
  )

Les deux versions sont égales. Enregistrons la deuxième version sous le nom GetCriticalError_Safe.mq5.

//+------------------------------------------------------------------+
//|                                        GetCriticalError_Safe.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| The safe printing of a message using the CHello object pointer     |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Printing a message using the CHello object, passed by reference  |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello &pobject)
  {
   DebugBreak();
   SafePrintStatus(GetPointer(pobject));
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      SafePrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

Si vous utilisez deux fonctions avec des mises en œuvre différentes (en transmettant la référence et le pointeur d'objet), cela permet d'assurer le travail sécurisé de la fonction surchargée.

Enfin, nous avons appris à utiliser les objets, transmis à la fonction en tant que et il est maintenant temps d'apprendre :

Quand a-t-on besoin de pointeurs ?

Les pointeurs d'objets permettent d'effectuer une gestion flexible du processus de création et de destruction d'objets, et vous permettent de créer des objets abstraits plus complexes. Cela rend le programme plus flexible.

Liste liée

Mais dans certains cas, une autre manière d'organiser les données est requise, la liste liée est l'une d'entre elles. La classe d'une liste liée CList est disponible dans la Bibliothèque Standard, ici nous allons présenter nos propres exemples. Une liste liée signifie que chaque élément de la liste est connecté aux éléments suivants et précédents, s'ils existent. Pour organiser de tels liens, il est pratique d'utiliser les pointeurs d'objets d'éléments de liste (ListItem).

Créons une classe, qui représentera un élément d'une liste, comme celle qui suit :

class CListItem
  {
private:
   int               m_ID;
   CListItem        *m_next;
   CListItem        *m_prev;
public:
                    ~CListItem();
   void              setID(int id){m_ID=id;}
   int               getID(){return(m_ID);}
   void              next(CListItem *item){m_next=item;}
   void              prev(CListItem *item){m_prev=item;}
   CListItem*        next(){return(m_next);}
   CListItem*        prev(){return(m_prev);}
  };

La liste elle-même sera organisée en une classe distincte :

//+------------------------------------------------------------------+
//| Linked list                                                      |
//+------------------------------------------------------------------+
class CList
  {
private:
   int               m_counter;
   CListItem        *m_first;
public:
                     CList(){m_counter=0;}
                    ~CList();
   void              addItem(CListItem *item);
   int               size(){return(m_counter);}
  };

La classe CList contient un pointeur m_first  du premier élément de la liste, un accès aux autres éléments de la liste est toujours disponible via les fonctions next() et prev() de la classe CListItem(). La classe CList a deux fonctions intéressantes. La première est la fonction d'ajout d'un nouvel élément à la liste.

//+------------------------------------------------------------------+
//| Adding of an item to the list                                    |
//+------------------------------------------------------------------+
CList::addItem(CListItem *item)
  {
//--- checking of a pointer, it should be correct
   if(CheckPointer(item)==POINTER_INVALID) return;
//--- increasing the number of list items
   m_counter++;
//--- if there isn't any items in the list
   if(CheckPointer(m_first)!=POINTER_DYNAMIC)
     {
      m_first=item;
     }
   else
     {
      //--- setting for first a pointer to the previous item
      m_first.prev(item);
      //--- saving a pointer of the current first item
      CListItem *p=m_first;
      //--- placing the input item on the place of the first element
      m_first=item;
      //--- for the first item in the list, setting a pointer to the next item 
      m_first.next(p);
     }
  }

Chaque élément ajouté à la liste devient le premier élément, le pointeur du premier élément précédent est stocké dans le champ m_next . Ainsi, un élément, ajouté en premier à la liste, sera en fin de liste. Le dernier élément ajouté sera le premier et son pointeur est stocké dans une variable m_first. La deuxième fonction intéressante est le destructeur ~CList(). Elle est appelée lorsque l'objet est détruit, et elle s’assure de la bonne destruction des objets de la liste. Cela peut se faire très simplement :

//+------------------------------------------------------------------+
//| List destructor                                                  |
//+------------------------------------------------------------------+
CList::~CList(void)
  {
   int ID=m_first.getID();
   if(CheckPointer(m_first)==POINTER_DYNAMIC) delete(m_first);
   Print(__FUNCTION__," The first item with ID =",ID," is destroyed");
  }

On peut voir, si les m_first contiennent le bon pointeur de l'élément de liste, seul le premier élément de liste est supprimé. Tous les autres éléments de la liste sont supprimés en cascade, car le destructeur de classe CListItem() produit à son tour la désinitialisation correcte de l'objet.

//+------------------------------------------------------------------+
//| Item destructor                                                  |
//+------------------------------------------------------------------+
CListItem::~CListItem(void)
  {
   if(CheckPointer(m_next)==POINTER_DYNAMIC)
     {
      delete(m_next);
      Print(__FUNCTION__," Removing an item with ID =",m_ID);
     }
   else
      Print(__FUNCTION__," The next item isn't defined for the item with ID=",m_ID);

  }

L'exactitude d'un pointeur est vérifiée à l'intérieur du destructeur, s'il est spécifié, l'objet avec le pointeur m_next est détruit à l'aide de l'opérateur delete(). Avant la destruction, le premier élément de la liste appelle le destructeur, qui supprime le deuxième élément, puis provoque la suppression du troisième élément et ainsi de suite jusqu'à la fin de la chaîne.

Le travail de la liste est montré dans le script SampleList.mq5. La déclaration d'une liste (une variable de CListType) est dans le OnStart () fonction. Cette liste sera créée et initialisée automatiquement. Le remplissage de la liste est effectué à l'intérieur d'une liste, et pour commencer, chaque élément de la liste est créé dynamiquement à l'aide de l'opérateur new puis il est ajouté à la liste. 

void OnStart()
  {
//---
   CList list;
   for(int i=0;i<7;i++)
     {
      CListItem *item=new CListItem;
      item.setID(i);
      list.addItem(item);
     }
     Print("There are ",list.size()," items in the list");
  }

Lancez le script et vous verrez les messages suivants dans le journal « Experts ».

2010.03.18 11:22:05 SampleList (EURUSD, H1) CList :: ~ CList Le premier élément avec ID=6 est détruit
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem :: ~ CListItem Suppression d'un élément avec ID = 6
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem :: ~ CListItem Suppression d'un élément avec ID = 5
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem :: ~ CListItem Suppression d'un élément avec ID = 4
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem :: ~ CListItem Suppression d'un élément avec ID = 3
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem :: ~ CListItem Suppression d'un élément avec ID = 2
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem :: ~ CListItem Suppression d'un élément avec ID = 1
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem :: ~ CListItem L'élément suivant n'est pas défini pour l'élément avec ID=0
2010.03.18 11:22:05 SampleList (EURUSD, H1) Il y a 7 éléments dans la liste

Si vous avez soigneusement étudié le code, il n'est pas surprenant que l'élément avec ID=0 n'ait pas l'élément suivant. Cependant, nous n'avons considéré qu'une seule raison, lorsque l'utilisation des pointeurs rend l'écriture de programmes pratique et lisible. Il y a une deuxième raison, connue sous le nom de

Polymorphisme

Il est souvent nécessaire d'implémenter la même fonctionnalité pour des objets différents, appartenant au même type. Par exemple, il existe des objets simples comme Ligne, Triangle, Rectangle et Cercle. Malgré le fait qu'ils ont une apparence différente, ils ont une caractéristique commune - ils peuvent être dessinés. Créons une classe de base CShape et utilisons-la pour la création de ses descendants pour chaque type de formes géométriques.


À des fins éducatives, la classe de base et ses descendants ont une fonctionnalité minimale.

//+------------------------------------------------------------------+
//| The base class for a Shape object                                |
//+------------------------------------------------------------------+
class CShape
  {
private:
   int               m_type;
public:
                     CShape(){m_type=0;}
   void              Draw();
   string            getTypeName(){return("Shape");}
  };
//+------------------------------------------------------------------+
//| The class for a Line object                                      |
//+------------------------------------------------------------------+
class CLine:public CShape
  {
private:
   int               m_type;
public:
                     CLine(){m_type=1;}
   void              Draw();
   string            getTypeName(){return("Line");}
  };
//+------------------------------------------------------------------+
//| The class for a Triangle object                                  |
//+------------------------------------------------------------------+
class CTriangle:public CShape
  {
private:
   int               m_type;
public:
                     CTriangle(){m_type=2;}
   void              Draw();
   string            getTypeName(){return("Triangle");}
  };
//+------------------------------------------------------------------+
//| The class for a Rectangle object                                 |
//+------------------------------------------------------------------+
class CRectangle:public CShape
  {
private:
   int               m_type;
public:
                     CRectangle(){m_type=3;}
   void              Draw();
   string            getTypeName(){return("Rectangle");}
  };
//+------------------------------------------------------------------+
//| The class for a Cirlce object                                    |
//+------------------------------------------------------------------+
class CCircle:public CShape
  {
private:
   int               m_type;
public:
                     CCircle(){m_type=4;}
   void              Draw();
   string            getTypeName(){return("Circle");}
  };

La classe parente CShape contient deux fonctions, qui sont surchargées dans ses descendants - un Draw() et getTypeName(). La fonction Draw() est destinée à dessiner une forme et la fonction getTypeName() renvoie une description de chaîne de la forme.

Créons un tableau *shapes[], qui contient des pointeurs de type de base CShape, et spécifie les valeurs d'un pointeur pour les différentes classes.

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- an array of pointers of objects of CShape type
   CShape *shapes[];
//--- resizing of an array 
   ArrayResize(shapes,5);
//--- filling of a pointers array
   shapes[0]=new CShape;
   shapes[1]=new CLine;
   shapes[2]=new CTriangle;
   shapes[3]=new CRectangle;
   shapes[4]=new CCircle;
//--- printing the type of each array element
   for(int i=0;i<5;i++)
     {
      Print(i,shapes[i].getTypeName());
     }
//--- deleting all objects in the array
   for(int i=0;i<5;i++) delete(shapes[i]);
  }

À l'intérieur du cycle for(), nous appelons la méthode getTypeName() pour chaque élément du tableau *shapes[]. La première fois, vous serez peut-être surpris du fait que la fonction getTypeName() d'une classe de base appelle chaque objet dans la liste, malgré le fait que chaque classe dérivée a sa propre mise en œuvre de la fonction getTypeName().

2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 4 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 3 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 2 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 1 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 0 Shape

L'explication de ce fait est la suivante : le tableau *shapes [] est déclaré comme un tableau de pointeurs de type CShape, et par conséquent, chaque objet de tableau appelle la méthode getTypeName() d'une classe de base, même si un descendant dispose d’une mise en œuvre différente. Pour appeler la fonction getTypeName() correspondant au type d'objet réel (descendant) au moment de l'exécution du programme, il est nécessaire de déclarer cette fonction dans une classe de base en tant que fonction virtuelle.

Ajoutons le mot-clé virtuel à la fonction getTypeName() dans la déclaration d'une classe CShape parent.

class CShape
  (
private:
   int m_type;
public:
                     CShape () (m_type = 0;)
   void Draw ();
   virtual string getTypeName () (return ("Shape");)
  )

et relancez le script. Maintenant, les résultats sont cohérents avec ceux qui sont attendus :

2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 4 Circle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 3 Rectangle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 2 Triangle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 1 Line
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 0 Shape

Ainsi, la déclaration d'une fonction virtuelle dans une classe de base permettait d'appeler la même fonction d'un descendant lors de l'exécution du programme. Nous pouvons maintenant mettre en œuvre la fonction Draw() complète pour chacune des classes dérivées.

Un exemple de son travail peut être trouvé dans le script DrawManyObjects.mq5 joint, qui montre des formes aléatoires sur le graphique.

Conclusion

Alors, il est temps de résumer. Dans MQL5, la création et la destruction des objets sont effectuées automatiquement, vous ne devez donc utiliser les pointeurs que s'ils sont vraiment nécessaires et si vous comprenez comment les utiliser.

Cependant, si vous ne pouvez pas le faire sans l'utilisation de pointeurs, assurez-vous de vérifier l'exactitude du pointeur avant son utilisation, en utilisant le CheckPointer() - il a été ajouté spécialement pour ces cas.

Une dernière chose : dans MQL5, les pointeurs ne sont pas de véritables pointeurs mémoire, comme autrefois utilisés en C++, vous ne devez donc pas les transmettre à la DLL sous forme de paramètres d'entrée.

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/36

Indicateurs personnalisés dans MQL5 pour débutants Indicateurs personnalisés dans MQL5 pour débutants
Tout nouveau sujet semble compliqué et difficile à apprendre pour un débutant. Les sujets que nous connaissons nous semblent très simples et clairs. Mais nous oublions simplement que nous avons tous dû étudier quelque chose à partir de zéro, et même notre langue maternelle. Il en va de même avec le langage de programmation MQL5 qui offre de larges possibilités de développer ses propres stratégies de trading - vous pouvez commencer à l'apprendre à partir de notions de base et d'exemples les plus simples. L'interaction d'un indicateur technique avec le terminal client MetaTrader 5 est envisagée dans cet article sur l'exemple de l'indicateur personnalisé simple SMA.
Présentation de MQL5 : Comment écrire un simple Expert Advisor et un indicateur personnalisé Présentation de MQL5 : Comment écrire un simple Expert Advisor et un indicateur personnalisé
Le langage de programmation MetaQuotes 5 (MQL5), inclus dans le terminal client MetaTrader 5, offre de nombreuses nouvelles possibilités et des performances supérieures par rapport au MQL4. Cet article vous aidera à vous familiariser avec ce nouveau langage de programmation. Les exemples simples de rédaction d'un Expert Advisor et d'un Indicateur personnalisé sont présentés dans cet article. Nous considérerons également quelques détails du langage MQL5, qui sont nécessaires pour comprendre ces exemples.
Traitement des événements de trade dans Expert Advisor à l'aide de la fonction OnTrade() Traitement des événements de trade dans Expert Advisor à l'aide de la fonction OnTrade()
MQL5 a apporté une multitude d'innovations, notamment le travail avec des événements de différents types (événements de minuterie, événements de trade, événements personnalisés, etc.). La capacité de gérer les événements vous permet de créer un tout nouveau type de programmes pour le trading automatique et semi-automatique. Dans cet article, nous considérerons les événements commerciaux et écrirons du code pour la fonction OnTrade(), qui traitera l'événement de trade.
MQL5 pour les débutants : Guide d'utilisation des indicateurs techniques dans Expert Advisors MQL5 pour les débutants : Guide d'utilisation des indicateurs techniques dans Expert Advisors
Afin d’obtenir les valeurs d’un indicateur intégré ou personnalisé dans un Expert Advisor, son descripteur doit d’abord être créé à l’aide de la fonction correspondante. Les exemples de l’article montrent comment utiliser tel ou tel indicateur technique lors de la création de vos propres programmes. L’article décrit les indicateurs générés dans le langage MQL5. Il est destiné à ceux qui n’ont pas beaucoup d’expérience dans le développement de stratégies de trading et offre des moyens simples et clairs de travailler avec des indicateurs en utilisant la bibliothèque de fonctions offerte.