Указатели объектов
В 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());
}
/*
1. pointer is POINTER_INVALID
2. pointer.Value()=3.141592653589793
3. pointer 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());
}
/*
1. pointer is POINTER_INVALID
2. pointer.Value()=3.141592653589793
3. pointer is POINTER_INVALID
*/
|
Для быстрой проверки на NULL следует использовать оператор "==". Например: ptr==NULL или ptr!=NULL.
Смотри также
Переменные, Инициализация переменных, Область видимости и время жизни переменных, Создание и уничтожение объектов