English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Порядок создания и уничтожения объектов в MQL5

Порядок создания и уничтожения объектов в MQL5

MetaTrader 5Примеры | 1 марта 2010, 13:59
6 267 18
MetaQuotes
MetaQuotes

О чем эта статья

Язык MQL5 позволяет писать в стиле объектно-ориентированного программирования (ООП), и это не только открывает новые возможности для создания собственных библиотек, но и позволяет использовать уже готовые, разработанные и протестированные классы других разработчиков. В стандартной библиотеке из поставки терминала MetaTrader 5 уже реализованы сотни классов, которые содержат тысячи методов.

Чтобы успешно использовать все преимущества ООП, необходимо уяснить некоторые тонкости, связанные с созданием и уничтожением объектов в программах на языке MQL5. В Документации кратко описано создание и уничтожение объектов в MQL5, эта статья проиллюстрирует указанный раздел примерами.

Инициализация и деинициализация глобальных переменных

Инициализация глобальных переменных производится сразу после загрузки mql5-программы и до вызова любой функции. При инициализации производится присвоение начальных значений переменным простых типов и вызывается конструктор для объектов, если он в них объявлен.

Рассмотрим простой пример, в котором объявим два класса CObjectA и CObjectB, каждый класс имеет конструктор и деструктор, состоящий только из функции Print(). Переменные, имеющие тип этих классов, объявим на глобальном уровне и запустим скрипт.

//+------------------------------------------------------------------+
//|                                         GlobalVar_TestScript.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//--- объявим объекты на глобальном уровне
CObjectA first;
CObjectB second;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   Print(__FUNCTION__);
  }

Результат выполнения скрипта выводится в журнал "Эксперты":

GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectA::ObjectA  Constructor
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectB::ObjectB  Constructor
GlobalVar_TestScript (EURUSD,H1)    13:05:07    OnStart
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectB::~ObjectB  Destructor
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectA::~ObjectA  Destructor

Из журнала видно,что порядок инициализации соответствует порядку объявления переменных в коде скрипта GlobalVar_TestScript.mq5, а деинициализация производится в обратном порядке перед выгрузкой mql5-программы.

Инициализация и деинициализация локальных переменных

Локальные переменные деинициализируются в конце блока программы, в котором они объявлены и в порядке, обратном их объявлению. Блок программы – это составной оператор, который может являться частью оператора выбора switch, цикла(for, while, do-while), телом функции или частью оператора if-else.

Инициализация локальных переменных происходит лишь в том случае, если выполнение программы доходит до объявления переменной. Если даже переменная объявлена в коде, но в ходе выполнения программы блок, в котором производится  объявление, не был выполнен, то такая переменная не создается, а следовательно, и не инициализируется.

Для иллюстрации к уже знакомым классам CObjectA и CObjectB добавим новый класс CObjectС. Объявления классов по-прежнему идут на глобальном уровне, но объявления переменных этих классов теперь разместим локально в функции OnStart().

Переменную класса CObjectA объявим безусловно сразу в первой исполняемой строке функции,  но объекты классов CObjectB и CObjectС объявим в разных блоках, которые выполняются в зависимости от значения input-переменной execute. В редакторе MetaEditor входные переменные mql5-программ подсвечиваются коричневым цветом.

//+------------------------------------------------------------------+
//|                                          LocalVar_TestScript.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property script_show_inputs
//--- input parameters
input bool     execute=false;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectC
  {
public:
                     CObjectC(){Print(__FUNCTION__," Constructor");}
                    ~CObjectC(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CObjectA objA;
//--- этот блок не выполнится, если execute==false
   if(execute)
     {
      CObjectB objB;
     }
//--- этот блок выполнится, если execute==false
   if(!execute)
     {
      CObjectC objC;
     }
  }
//+------------------------------------------------------------------+

Результат:

LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectA::CObjectA  Constructor
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectC::CObjectC  Constructor
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectC::~CObjectC  Destructor
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectA::~CObjectA  Destructor

C каким бы значением входного параметра execute мы не запускали этот скрипт, первым всегда автоматически инициализируется объект класса CObjectA, за ним автоматически инициализируется тот объект( objB или objC), чей блок объявления был исполнен в зависимости от истинности execute. По умолчанию, этот параметр имеет значение false, в этом случае после инициализации переменной objA происходит инициализация переменной objC. Факт инициализации и деинициализации хорошо виден по выполнению конструктора и деструктора.

Но каков бы ни был порядок инициализации (независимо от параметра execute), деинициализация переменных сложных типов всегда производится в порядке, обратном порядку их инициализации. Это общее свойство как для локальных, так и для глобальных автоматически создаваемых объектов классов. В этом отношении разницы между ними нет.

Инициализация и деинициализация динамически создаваемых объектов

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

Указатели могут быть объявлены на локальном и/или глобальном уровне, и при этом могут быть проинициализированы пустым значением NULL порожденного типа. Создание объекта производится только в  тот момент, когда к указателю объекта применяется оператор new, и не связано с моментом объявления указателя объекта. 

Уничтожение динамического объекта производится посредством оператора delete, поэтому необходимо самостоятельно позаботиться об этом. Рассмотрим пример, в котором на глобальном уровне объявим две переменные типа CObjectA и CObjectB, а также одну переменную с указателем объекта типа CObjectC - назовем ее pObjectC.

//+------------------------------------------------------------------+
//|                                       GlobalVar_TestScript_2.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectC
  {
public:
                     CObjectC(){Print(__FUNCTION__," Constructor");}
                    ~CObjectC(){Print(__FUNCTION__," Destructor");}
  };
CObjectC *pObjectC;
CObjectA first;
CObjectB second;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   pObjectC=new CObjectC;
   Print(__FUNCTION__);
   delete(pObjectC);
  }
//+------------------------------------------------------------------+

Несмотря на то, что указатель pObjectC  динамически созданного объекта объявлен ранее статических переменных first и second, инициализация этого объекта производится только в тот момент, когда объект создается оператором new. В данном примере оператор new расположен в функции OnStart().

GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectA::CObjectA  Constructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectB::CObjectB  Constructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectC::CObjectC  Constructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    OnStart
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectC::~CObjectC  Destructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectB::~CObjectB  Destructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectA::~CObjectA  Destructor

В тот момент, когда выполнение программы внутри функции OnStart() доходит до оператора

   pObjectC=new CObjectC;

происходит инициализация объекта и вызов конструктора для него. Затем происходит выполнение строчки

   Print(__FUNCTION__);

которая выводит в журнал строчку

GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    OnStart

и затем динамически созданный объект уничтожается вызовом для него оператора delete:

   delete(pObjectC);

Таким образом, объекты создаются динамически в момент создания оператором new и уничтожаются оператором delete.

Обязательное требование: все объекты, созданные выражением указатель_объекта=new Имя_Класса, обязательно должны быть впоследствии уничтожены оператором delete(указатель_объекта). Если по каким то причинам динамически созданный объект при выходе управления из блока, где он был инициализирован, не был уничтожен оператором delete, то об этом будет выведено сообщение в журнал "Эксперты".


Удаление динамически создаваемых объектов

Как уже говорилось, каждый динамически создаваемый объект инициализируется оператором new и должен обязательно явно уничтожаться оператором delete. Но при этом необходимо помнить, что оператор new создает объект и возвращает указатель этого объекта. Сам созданный объект не находится в переменной, содержащей указатель объекта. Можно объявить несколько указателей, и всем им присвоить указатель одного и того же объекта.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_1.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  простой класс                                                   |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- объявим первый массив указателей на объект
   CItem* array1[5];
//--- объявим второй массив указателей на объект
   CItem* array2[5];
//--- теперь заполним массивы в цикле
   for(int i=0;i<5;i++)
     {
      //--- указатель для первого массива создадим оператором new
      array1[i]=new CItem;
      //--- указатель для второго массива создадим скопируем из первого массива
      array2[i]=array1[i];
     }
   // мы "забыли" удалить объекты перед выходом из функции,смотрим закладку "Эксперты"
  }
//+------------------------------------------------------------------+

Будет выведено сообщение о том, что остались неудаленные объекты. Но этих объектов будет не 10, как можно ошибочно ожидать, а 5. Ведь создали мы оператором new только 5 объектов.

(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    5 undeleted objects left

Если даже для динамически созданного объекта деструктор не вызывается (объект не уничтожается оператором delete), то память все равно будет освобождена. Но при этом в журнал "Эксперты" выводится сообщение, что объект не удален. Это позволяет выявить факт неправильного управления объектами в программе и устранить ошибку.

Изменим немного пример – попытаемся уничтожить указатели в каждом из двух массивов указателей array1 и array2.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_2.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  простой класс                                                   |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- объявим первый массив указателей на объект
   CItem* array1[5];
//--- объявим второй массив указателей на объект
   CItem* array2[5];
//--- теперь заполним массивы в цикле
   for(int i=0;i<5;i++)
     {
      //--- указатель для первого массива создадим оператором new
      array1[i]=new CItem;
      //--- указатель для второго массива скопируем из первого массива
      array2[i]=array1[i];
     }
//--- уничтожим объекты по указателям второго массива
   for(int i=0;i<5;i++) delete(array2[i]);
//--- теперь попытаемся уничтожить объекты по указателям первого массива
   for(int i=0;i<5;i++) delete(array2[i]);
// получим сообщения в закладке "Эксперты" о попытках удаления для некорректного указателя 
  }
//+------------------------------------------------------------------+

Результат выполнения скрипта в закладке "Эксперты" теперь выглядит по-другому.

(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    delete invalid pointer
(GBPUSD,H1)    15:02:48    delete invalid pointer
(GBPUSD,H1)    15:02:48    delete invalid pointer
(GBPUSD,H1)    15:02:48    delete invalid pointer
(GBPUSD,H1)    15:02:48    delete invalid pointer

Созданные объекты CItem были успешно удалены в первом цикле for(), все попытки удаления уже несуществующих объектов во втором цикле вызвали появление сообщений о некорректных указателях. Удалять динамически созданный объект всегда нужно однократно, и перед использованием любого указателя объекта желательно его проверять функцией CheckPointer().

Проверка указателя объекта функцией CheckPointer()

Функция CheckPointer() служит для проверки указателей и позволяет определить тип указателя. Возможны две ошибки при работе с динамически созданными объектами:

  • неудаление в конце блока выполнения;
  • попытка удаления уже несуществующего объекта.

Рассмотрим еще один пример, который демонстрирует взаимоотношение объектов. Спроектируем два класса, один класс - CItemArray - содержит в себе массив указателей другого класса: CItem.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_3.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  простой класс                                                   |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| класс для хранения массива указателей CItem                      |
//+------------------------------------------------------------------+
class CItemArray
  {
private:
   CItem            *m_array[];
public:
                     CItemArray(){Print(__FUNCTION__," Constructor");}
                    ~CItemArray(){Print(__FUNCTION__," Destructor");Destroy();}
   void               SetArray(CItem &array[]);
protected:
   void               Destroy();
  };
//+------------------------------------------------------------------+
//|  заполнение массива указателей                                   |
//+------------------------------------------------------------------+
CItemArray::SetArray(CItem &array[])
  {
   int size=ArraySize(array);
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++)m_array[i]=GetPointer(array[i]);
  }
//+------------------------------------------------------------------+
//|  освобождение                                                    |
//+------------------------------------------------------------------+
CItemArray::Destroy(void)
  {
   for(int i=0;i<ArraySize(m_array);i++)
     {
      if(CheckPointer(m_array[i])!=POINTER_INVALID)
        {
         if(CheckPointer(m_array[i])==POINTER_DYNAMIC) delete(m_array[i]);
        }
      else Print("Invalid pointer to delete");
     }
  }

Сами по себе классы никакой ошибки не содержат, но вот их применение может также принести сюрпризы. Первый вариант скрипта:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CItemArray items_array;
   CItem array[5];
   items_array.SetArray(array);
  }

При запуске этого варианта получаем следующие сообщения:

(GBPUSD,H1)    16:06:17    CItemArray::CItemArray  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItemArray::~CItemArray  Destructor
(GBPUSD,H1)    16:06:17    Invalid pointer to delete
(GBPUSD,H1)    16:06:17    Invalid pointer to delete
(GBPUSD,H1)    16:06:17    Invalid pointer to delete
(GBPUSD,H1)    16:06:17    Invalid pointer to delete

Так как в функции первым идет объявление переменной класса CItemArray, то первой производится инициализация именно этой переменной и вызывается конструктор  класса. Затем объявляется массив array[5], содержащий указатели объектов класса CItem. Поэтому мы видим 5 сообщений об инициализации каждого такого объекта.

Последней строчкой этого простого скрипта производится копирование указателей из массива array[5] во внутренний массив указателей объекта items_array.

   items_array.SetArray(array);

На этом выполнение скрипта заканчивается и производится автоматическое уничтожение автоматически созданных объектов. Первым уничтожается тот объект, который был инициализирован последним - это массив указателей array[5]. Это подтверждено в Журнале пятью записями о вызове деструктора класса CItem. Затем идет сообщение о вызове деструктора для объекта items_array, так как он был проинициализирован непосредственно перед переменной array[5].

Но деструктор класса CArrayItem вызывает защищенную функцию Destroy(), в которой производится попытка уничтожения объектов CItem через указатели в m_array[] через оператор delete. Предварительно производится проверка указателя, и если указатель является некорректным, то попытка удаления не производится, а выводится сообщение "Invalid pointer to delete".

Всего таких сообщений в Журнале 5, то есть все указатели в массиве m_array[] оказались некорректными. Это произошло потому, что эти объекты данных указателей уже были деинициализрованы при деинициализации массива array[].

Изменим скрипт совсем немного – поменяем местами объявление переменных items_array и items_array[] местами (См. 'LocalVar_TestScript_4.mq5').

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CItem array[5];
   CItemArray items_array;
   items_array.SetArray(array);
  }

Исправленный вариант никаких сообщений об ошибках не выдает. Первой теперь был деинициализирована переменная items_array, так как была объявлена последней. При ее деинициализации был вызван деструктор класса ~CItemArray(), в котором в свою очередь вызывается функция Destroy().

При таком порядке объявления уничтожение items_array происходит до уничтожения массива объектов array[5]. В функции Destroy(), которая вызывается из деструктора items_array, работа ведётся с указателями на ещё существующие объекты, поэтому никаких ошибок не возникает.

Третий пример правильного удаления динамически созданных объектов можно посмотреть в разделе функции GetPointer(). В приведенном там примере явным образом вызывается функция Destroy() для обеспечения нужного порядка удаления объектов.

Заключение

Как видите, ничего сложного в понимании механизмов создания и уничтожения объектов нет. Достаточно выполнить все приведенные в статье примеры, и в дальнейшем вы сами сможете придумывать свои варианты взаимодействия между автоматически и динамически созданными объектами.

Обязательно проверяйте свои классы на корректность удаления объектов и правильно проектируйте деструкторы, чтобы не возникало ошибок при попытке доступа к некорректным указателям. Помните, что в случае использования динамически создаваемых объектов оператором new, необходимо уметь правильно уничтожать эти объекты оператором delete.

В этой статье мы изучили только порядок создания и уничтожения объектов в MQL5. Как организовать безопасную работу с указателями объектов- тема отдельной статьи.

Последние комментарии | Перейти к обсуждению на форуме трейдеров (18)
kashi_ann
kashi_ann | 13 февр. 2020 в 12:04
Artyom Trishkin:

Это азы.

Спасибо) Азы - это самое важное)

И спасибо, что не поленились ответить)
Andrey Khatimlianskii
Andrey Khatimlianskii | 13 февр. 2020 в 12:07
kashi_ann:

так... получается следующие записи...

верно?

Так совпало, что да, но вы явно хотели использовать ==, а не =.

kashi_ann
kashi_ann | 13 февр. 2020 в 12:09
Andrey Khatimlianskii:

Так совпало, что да, но вы явно хотели использовать ==, а не =.

Не))) со смыслом и различием = и == мне еще предстоит разобраться) Я в самом начале пути)

Artyom Trishkin
Artyom Trishkin | 13 февр. 2020 в 18:51
kashi_ann:

Не))) со смыслом и различием = и == мне еще предстоит разобраться) Я в самом начале пути)

"=" это оператор присвоения, а "==" - оператор сравнения.
vp999369
vp999369 | 4 окт. 2023 в 18:49

<цитата>

@При таком порядке объявления уничтожение items_array происходит до уничтожения массива объектов array[5]. В функции Destroy(), которая вызывается из деструктора items_array, работа ведётся с указателями на ещё существующие объекты, поэтому никаких ошибок не возникает.@ </цитата>

Всем привет!

Вот тут я логики вообще не понял. =) 

Ведь в методе Destroy мы сравниваем тип нашего указателя с динамическим типом. А наш тип автоматический. Следовательно до оператора delete дело просто не доходит.

Но он и не нужен, ведь автоматически созданные объекты удаляются автоматически.

И последнее, в файле забыли указать тип возвращаемого значения void для методов Destroy и SetArray на этапе реализации методов за пределами Класса из-за чего выбивает ошибку.

Новички (как я) могут пугаться =)


void CItemArray::Destroy(void)
  {
   for(int i=0;i<ArraySize(m_array);i++)
     {
      if(CheckPointer(m_array[i])!=POINTER_INVALID)
        {
         if(CheckPointer(m_array[i])==POINTER_DYNAMIC) delete(m_array[i]);
        }
      else Print("Invalid pointer to delete");
     }
  }
Пользовательские индикаторы в MQL5 для начинающих Пользовательские индикаторы в MQL5 для начинающих
Любой новый предмет для новичка с первого взгляда кажется сложным для понимания. Нам кажется простым и ясным то, что мы уже знаем. Но мы просто не помним, что всем нам когда-то приходилось изучать с нуля, даже родной язык, на котором мы разговариваем. Так и язык MQL5, таящий в себе огромные возможности для написания торговых стратегий, можно начать изучать с базовых понятий и примеров. В этой статье на примере пользовательского индикатора SMA рассматривается взаимодействие технического индикатора с клиентским терминалом MetaTrader 5.
MQL5.community - Памятка пользователя MQL5.community - Памятка пользователя
Вы недавно зарегистрировались и у вас возникли вопросы: Как вставить картинку в сообщение на форуме, как красиво оформить исходный код MQL5, где находятся ваши Личные сообщения? В этой статье мы подготовили для вас несколько практических советов, которые помогут быстрее освоиться на сайте MQL5.community и позволят в полной мере воспользоваться доступными функциональными возможностями.
MQL5 для "чайников": Получение значений технических индикаторов в своих экспертах MQL5 для "чайников": Получение значений технических индикаторов в своих экспертах
Для получения в торговом советнике значений встроенного или пользовательского индикатора, необходимо предварительно создать его хендл с помощью соответствующей функции. На примерах показано, как воспользоваться тем или иным техническим индикатором при разработке своих программ. Речь идёт о индикаторах, которые непосредственно встроены в язык MQL5. Статья предназначена для начинающих разработчиков торговых стратегий и предлагает простые и ясные способы работы с индикаторами с использованием приложенной библиотеки функций.
МetaTrader 5. Экспорт котировок в .NET приложение, используя WCF сервисы МetaTrader 5. Экспорт котировок в .NET приложение, используя WCF сервисы
Вам необходимо организовать трансляцию котировок из MetaTrader 5 в собственное приложение? Связка MQL5-DLL позволяет создавать подобные решения. В статье продемонстрирован один из способов трансляции котировок из MetaTrader 5 в приложения, написанные на .NET. Мне было рациональнее, интереснее и проще реализовать экспорт котировок именно с использованием этой платформы. К сожалению, с выходом "пятерки" поддержки .Net также не появилось, поэтому по старинке будем использовать как прослойку win32 dll с поддержкой .NET.