Указатели объектов

В MQL5 существует возможность динамически создавать объекты сложного типа. Это делается при помощи оператора new, который возвращает описатель созданного объекта. Описатель имеет размер 8 байт. Синтаксически описатели объектов в MQL5 похожи на указатели в C++.

Пример:

MyObject* hobject= new MyObject();

В отличие от C++, переменная hobject из вышеприведенного примера не является указателем на память, а является дескриптором объекта. Кроме того, в языке MQL5 все объекты в параметрах функции обязательно должны передаваться по ссылке. Примеры передачи объектов в качестве параметра функции:

class Foo
  {
public:
   string            m_name;
   int               m_id;
   static int        s_counter;
   //--- конструкторы и деструкторы
                     Foo(void){Setup("noname");};
                     Foo(string name){Setup(name);};
                    ~Foo(void){};
   //--- инициализируем объект Foo
   void              Setup(string name)
     {
      m_name=name;
      s_counter++;
      m_id=s_counter;
     }
  };
int Foo::s_counter=0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- объявим объект как переменную с автоматическим созданием
   Foo foo1;
//--- вариант передачи объекта по ссылке
   PrintObject(foo1);
 
//--- объявим указатель на объект и создадим его с помощью оператора 'new'
   Foo *foo2=new Foo("foo2");
//--- вариант передачи указателя на объект по ссылке
   PrintObject(foo2); // указатель на объект автоматически преобразуется компилятором
 
//--- объявим массив объектов Foo
   Foo foo_objects[5];
//--- вариант передачи массива объектов
   PrintObjectsArray(foo_objects); // отдельная функция для передачи массива объектов
 
//--- объявим массив указателей на объекты типа Foo
   Foo *foo_pointers[5];
   for(int i=0;i<5;i++)
      foo_pointers[i]=new Foo("foo_pointer");
//--- вариант передачи массива указателей
   PrintPointersArray(foo_pointers); // отдельная функция для передачи массива указателей
 
//--- перед завершением работы обязательно удаляем объекты, созданные как указатели
   delete(foo2);
//--- удаляем массив указателей
   int size=ArraySize(foo_pointers);
   for(int i=0;i<5;i++)
      delete(foo_pointers[i]);
//---   
  }
//+------------------------------------------------------------------+
//|  Объекты всегда передаются по ссылке                             |
//+------------------------------------------------------------------+
void PrintObject(Foo &object)
  {
   Print(__FUNCTION__,": ",object.m_id," Object name=",object.m_name);
  }
//+------------------------------------------------------------------+
//| Передача массива объектов                                        |
//+------------------------------------------------------------------+
void PrintObjectsArray(Foo &objects[])
  {
   int size=ArraySize(objects);
   for(int i=0;i<size;i++)
      PrintObject(objects[i]);
  }
//+------------------------------------------------------------------+
//| Передача массива указателей на объект                            |
//+------------------------------------------------------------------+
void PrintPointersArray(Foo* &objects[])
  {
   int size=ArraySize(objects);
   for(int i=0;i<size;i++)
      PrintObject(objects[i]);
  }
//+------------------------------------------------------------------+

 

Проверка указателя перед использованием

Попытка обращения к некорректному указателю приводит к критическому завершению программы. Для проверки указателя перед использованием служит функция CheckPointer. Указатель может быть некорректным в следующих случаях:

  • указатель равен NULL;
  • объект был уничтожен при помощи оператора delete.

Данную функцию можно использовать как проверку указателя на корректность. Значение, отличное от нуля, гарантирует, что по этому указателю можно получить доступ к данным.

class CMyObject
 {
protected:
  double             m_value;
public:
                     CMyObject(void);
                     CMyObject(double value) {m_value=value;};
                    ~CMyObject(void){};
  //---
  double             Value(void) {return(m_value);}
 };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
 {
//--- создадим неинициализованный объект
  CMyObject *pointer;
  if(CheckPointer(pointer)==POINTER_INVALID)
    Print("1. pointer is "EnumToString(CheckPointer(pointer)));
  else
    Print("1. pointer.Value()="pointer.Value());
 
//--- инициализируем указатель
  pointer=new CMyObject(M_PI);
  if(CheckPointer(pointer)==POINTER_INVALID)
    Print("2. pointer is "EnumToString(CheckPointer(pointer)));
  else
    Print("2. pointer.Value()="pointer.Value());
 
//--- удалим объект
  delete(pointer);
  if(CheckPointer(pointer)==POINTER_INVALID)
    Print("3. pointer is "EnumToString(CheckPointer(pointer)));
  else
    Print("3. pointer.Value()="pointer.Value());
 }
/*
   1pointer is POINTER_INVALID
   2pointer.Value()=3.141592653589793
   3pointer is POINTER_INVALID
*/

Для быстрой проверки указателя можно также использовать оператор "!" (LNOT), который проверяет его на валидность через неявный вызов функции CheckPointer. Это позволяет писать код более кратко и наглядно. Вот как будут выглядеть проверки из предыдущего примера:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
 {
//--- создадим неинициализованный объект
  CMyObject *pointer;
  if(!pointer)
    Print("1. pointer is "EnumToString(CheckPointer(pointer)));
  else
    Print("1. pointer.Value()="pointer.Value());
 
//--- инициализируем указатель
  pointer=new CMyObject(M_PI);
  if(!pointer)
    Print("2. pointer is "EnumToString(CheckPointer(pointer)));
  else
    Print("2. pointer.Value()="pointer.Value());
 
//--- удалим объект
  delete(pointer);
  if(!pointer)
    Print("3. pointer is "EnumToString(CheckPointer(pointer)));
  else
    Print("3. pointer.Value()="pointer.Value());
 }
/*
   1pointer is POINTER_INVALID
   2pointer.Value()=3.141592653589793
   3pointer is POINTER_INVALID
*/

Для быстрой проверки на NULL следует использовать оператор "==". Например: ptr==NULL или ptr!=NULL.

Смотри также

Переменные, Инициализация переменных, Область видимости и время жизни переменных, Создание и уничтожение объектов