Discussion de l'article "Utilisation des pointeurs d'objet dans MQL5" - page 3

 
N'utilisez pas de pointeurs dans ce cas.
 
Barbarian2:

Je ne comprends toujours pas ce que sont les pointeurs et les références dans MQL5 et maintenant dans MQL4. Quelle est la différence entre le passage par référence et le pointeur, si ce n'est du code supplémentaire ? Il y a une différence en C++, mais quelle est-elle ici ? S'il n'est pas difficile d'écrire plus en détail.

Le passage par référence nécessite que l'objet passé par référence soit initialisé. Le passage par un pointeur n'a pas cette restriction :

class CShape
{
   public:
      string name;
};

void OnInit()
{
   CShape* shape = NULL; 
   TestShapePointer(shape);
   //Critical Error!
   TestShapeRef(shape);
   
}

void TestShapeRef(CShape& shape)
{
   printf(shape.name);
}

void TestShapePointer(CShape* shape)
{
   if(CheckPointer(shape) != POINTER_INVALID)
      printf(shape.name);
}

Lors de l'appel de la fonction TestShapeRef, le code plantera car la forme n'est pas initialisée. En revanche, dans la fonction TestShapePointer, des contrôles constants sont nécessaires, que l'objet passé soit initialisé ou non. Par conséquent, il convient de suivre la règle empirique :

Si l'existence de l'objet doit être garantie au moment où il est transmis à la fonction, utilisez une passe "&". Si la fonction requiert un objet dont l'état est indéfini, utilisez le passage par le pointeur "*" et, à l'intérieur de cette fonction, vérifiez la validité du pointeur passé.

Il y a une autre nuance subtile que vous devez garder à l'esprit. Reprenons l'exemple précédent :

class CShape
{
   public:
      string name;
};

void OnInit()
{
   CShape* shape = NULL; 
   TestShapePointer(shape);
   printf(shape.name); //ERROR (!?)
}

void TestShapePointer(CShape* shape)
{
   if(CheckPointer(shape) == POINTER_INVALID)
      shape = new CShape();
   printf(shape.name);
}
Ce programme fonctionnera-t-il correctement ? Non, il se terminera par l'erreur "invalid pointer access" à la ligne printf(shape.name) ; //ERROR ( !?) , malgré le fait que nous semblions être assurés de créer un objet dans la fonction TestShapePointer. Le fait est qu'en réalité, une référence NULL a été transmise à la place de la forme. En d'autres termes, la forme à l'intérieur de la fonction et la forme passée sont des objets différents! Ainsi, après avoir quitté la fonction, la forme est toujours égale à NULL, et le pointeur de forme à l'intérieur de la fonction est perdu (effacé dans la pile). Donc.
 

Est-il possible de créer un tableau avec des objets de types différents ?

Prenons cet exemple :

//+------------------------------------------------------------------+
//|#Test.mq5 |
//| Copyright 2014, MetaQuotes Software Corp.
//|http ://www.mql5.com
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//---
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//---
CChartObjectRectLabel rect_label;
CChartObjectButton    button;
CChartObjectEdit      edit;
//---
CChartObject *objects[];
//+------------------------------------------------------------------+
//| Fonction de démarrage du programme de script|
//+------------------------------------------------------------------+
void OnStart()
  {
   edit.Create(0,"edit_1",0,20,20,100,20);
   button.Create(0,"button_1",0,20,40,100,20);
   rect_label.Create(0,"rect_label_1",0,20,60,100,20);
//---
   AddObjectToArray(edit);
   AddObjectToArray(button);
   AddObjectToArray(rect_label);
//---
   //objets[0]. // Comment accéder aux méthodes de la classe CChartObjectEdit ?
   //objets[1]. // Comment accéder aux méthodes de la classe CChartObjectButton ?
   //objets[2]. // Comment accéder aux méthodes de la classe CChartObjectRectLabel ?
//---
   while(!IsStopped()) Sleep(1000);
  }
//+------------------------------------------------------------------+
//| Renvoie le nombre d'objets|
//+------------------------------------------------------------------+
int ElementsTotal()
  {
   return(ArraySize(objects));
  }
//+------------------------------------------------------------------+
//|| Ajoute un objet au tableau|
//+------------------------------------------------------------------+
void AddObjectToArray(CChartObject &object)
  {
   int size=ElementsTotal();
//---
   ArrayResize(objects,size+1);
   objects[size]=GetPointer(object);
//---
   if(CheckPointer(objects[size])!=POINTER_INVALID)
      Print(__FUNCSIG__," >>> array["+IntegerToString(size)+"]: ",objects[size].Name(),
            "; object type: ",ObjectTypeToString(objects[size].Type()));
  }
//+------------------------------------------------------------------+
//| Traduit le type d'objet en une chaîne de caractères|
//+------------------------------------------------------------------+
string ObjectTypeToString(int type)
  {
   string str="";
//---
   switch((ENUM_OBJECT)type)
     {
      case OBJ_EDIT            : return("edit");            break;
      case OBJ_BUTTON          : return("button");          break;
      case OBJ_RECTANGLE_LABEL : return("rectangle label"); break;
     }
//---
   return("undefined object type");
  }
//+------------------------------------------------------------------+

//---

Comment accéder aux méthodes des classes héritées ?

 
tol64:

Comment accéder aux méthodes des classes héritières ?

Avez-vous essayé d'utiliser le type cible (casting) ?
 
Rosh:
Avez-vous essayé de lancer un type de cible ?
Non. C'est la première fois que j'en entends parler. Où puis-je trouver des informations à ce sujet ?
 
tol64:
Non. C'est la première fois que j'en entends parler. Où puis-je trouver des informations à ce sujet ?
Il s'agit d'un type d'adjectif courant, dont voici un exemple :

//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CFoo
  {
  };
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CBar : public CFoo
  {
public:
   void func(int x) { Print("Hello from Bar ",x); }
  };
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void func(CFoo *foo)
  {
//--- appel avec casting directement du pointeur au type de base
   ((CBar*)foo).func(1);
//--- appel avec conversion vers le type cible et enregistrement d'une copie du pointeur avec le type requis
   CBar *b=(CBar *)foo;
   b.func(2);
//--- supprimer la classe créée dynamiquement
   delete foo;
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void OnStart()
  {
   func(new CBar);
  }
//+------------------------------------------------------------------+
 
mql5:
Le type de conversion habituel, dont voici un exemple :

Merci. Je me pencherai sur la question.
 
Rosh:
Avez-vous essayé de lancer le type cible ?
Merde, et tu parles de sécurité linguistique après ça ?
 
TheXpert:
Merde, et vous parlez de sécurité linguistique après ça ?
Tu as trouvé une faille de sécurité ? )
 

Ne serait-il pas préférable d'utiliser le polymorphisme?

Voici comment cela se passe :

CChartObject *ptr_rect_label;
CChartObject *ptr_button;
CChartObject *ptr_edit;
CChartObject *objects[];
//---
ptr_rect_label=new CChartObjectRectLabel;
ptr_button=new CChartObjectButton;
ptr_edit=new CChartObjectEdit;