Обсуждение статьи "Когда нужно использовать указатели в MQL5" - страница 3

 
Не используйте указатели в таком случае.
 
Barbarian2:

Я так и не понял про указатели и ссылки в MQL5 и теперь уже в MQL4. В чем разница передачи по ссылке и указателю кроме лишнего кода? В С++ разница есть, а тут в чем? Если не сложно напишите более развернуто.

Передача по ссылке требует, чтобы объект переданный по ссылке, был инициализирован. Передача по указателю этого ограничения не имеет:

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);
}

 При вызове функции  TestShapeRef код аварийно завершит свою работу т.к. shape не инициализирован. С другой стороны, внутри функции TestShapePointer требуются постоянные проверки, инициализирован ли переданный объект или нет. Поэтому следуйте импирическому правилу:

Если объект на момент передачи его функции должен гарантированно существовать - используйте передачу по ссылке "&". Если для работы функции требуется объект, чье состояние неопределенно - используйте передачу по указателю "*", а внутри этой функции делайте проверку на валидность переданного указателя.

 Есть еще один тонкий ньюанс, о котором нужно помнить. Рассмотрим предыдущий пример:

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);
}
Будет ли эта программа работать корректно? Нет, она завершиться ошибкой "invalid pointer access" на строке  printf(shape.name); //ERROR (!?) , не смотря на то, что мы вроде бы гарантированно создаем объект в функции  TestShapePointer. Дело в том, что на самом деле вместо shape была передана NULL ссылка. Т.е. shape внутри функции и переданный shape это разные объекты! Таким образом, после выхода из функции shape по-прежнему равен NULL, а указатель shape внутри функции потерян (очищен в стеке). Так вот.
 

А можно ли создать массив с объектами разных типов?

Допустим вот такой пример:

//+------------------------------------------------------------------+
//|                                                        #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[];
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
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);
//---
   //objects[0]. // Как получить доступ к методам класса CChartObjectEdit ?
   //objects[1]. // Как получить доступ к методам класса CChartObjectButton ?
   //objects[2]. // Как получить доступ к методам класса CChartObjectRectLabel ?
//---
   while(!IsStopped()) Sleep(1000);
  }
//+------------------------------------------------------------------+
//| Возвращает количество объектов                                   |
//+------------------------------------------------------------------+
int ElementsTotal()
  {
   return(ArraySize(objects));
  }
//+------------------------------------------------------------------+
//| Добавляет объект в массив                                        |
//+------------------------------------------------------------------+
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()));
  }
//+------------------------------------------------------------------+
//| Переводит тип объекта в строку                                   |
//+------------------------------------------------------------------+
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");
  }
//+------------------------------------------------------------------+

//---

Как получить доступ к методам классов наследников? 

 
tol64:

Как получить доступ к методам классов наследников? 

Кастинг к целевому типу пробовали?
 
Rosh:
Кастинг к целевому типу пробовали?
Нет. Впервые слышу. Где можно об этом почитать? 
 
tol64:
Нет. Впервые слышу. Где можно об этом почитать? 
Обычное приведение типа, вот пример:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CFoo
  {
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CBar : public CFoo
  {
public:
   void func(int x) { Print("Hello from Bar ",x); }
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void func(CFoo *foo)
  {
//--- вызов с приведением напрямую от указателя на базовый тип
   ((CBar*)foo).func(1);
//--- вызов с приведением к целевому типу с сохранением копии указателя с нужным типом
   CBar *b=(CBar *)foo;
   b.func(2);
//--- удалим динамически созданный класс
   delete foo;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   func(new CBar);
  }
//+------------------------------------------------------------------+
 
mql5:
Обычное приведение типа, вот пример:

Спасибо. Буду разбираться.
 
Rosh:
Кастинг к целевому типу пробовали?
Блин, и вы после этого про безопасность языка говорите?
 
TheXpert:
Блин, и вы после этого про безопасность языка говорите?
Ты нашёл дыру в безопасности? )
 

А не лучше воспользоваться полиморфизмом?

Примерно так:

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;
Причина обращения: