Объявление переменных
Перед тем как использовать переменные они должны быть объявлены. Для идентификации переменных используются уникальные имена. Описания переменных используются для их определения и объявления типов. Описание не является оператором.
Простыми типами являются:
char, short, int, long, uchar, ushort, uint, ulong – целые числа;
color – целое число, представляющее RGB-цвет;
datetime – дата и время, беззнаковое целое число, содержащее количество секунд, прошедших с 0 часов 1 января 1970 года;bool – логические значения true и false;
double – числа двойной точности с плавающей точкой;
float – числа одинарной точности с плавающей точкой;
string – символьные строки.
Примеры:
string strInfoBox; int iOrdersNum; double dSymbolPrice; bool bLog; datetime tBegin_Data = D'2017.01.01 00:00'; color cModify_Color = C'0x17,0xA6,0xC8';
Сложные или составные типы:
Структуры
– это составные типы данных, построенные с использованием других типов.
struct MyTime { int hour; // 0-23 int minute; // 0-59 int second; // 0-59 }; ... MyTime strTime; // переменная типа объявленной ранее структуры MyTime
До тех пор пока структура не объявлена, нельзя объявлять переменные типа структуры.
Массивы
-это разновидность объекта, которая предназначена для хранения пронумерованных значений и предлагает дополнительные методы для удобного манипулирования такой коллекцией. Они обычно используются для хранения упорядоченных коллекций данных, например – списка товаров на странице, студентов в группе и т.п.
По сути массив — это индексированная совокупность однотипных данных:
int a[50]; // Одномерный массив из 50 целых чисел.
double m[7][50]; // Двухмерный массив из семи массивов,
// каждый из которых состоит из 50 чисел.
MyTime t[100]; // массив содержащий элементы типа MyTime
Индексом массива может быть только целое число. Допускаются не более чем четырехмерные массивы. Нумерация элементов массива начинается с 0. Последний элемент одномерного массива имеет номер на 1 меньший, чем размер массива, то есть обращение к последнему элементу массива из 50 целых чисел будет выглядеть как a[49]. То же самое относится и к многомерным массивам – индексация одного измерения производится от 0 до размер измерения-1. Последний элемент двумерного массива из примера будет выглядеть как m[6][49].
Статические массивы не могут быть представлены в виде таймсерий, то есть к ним не применима функция ArraySetAsSeries(), которая устанавливает доступ к элементам массива от конца массива к его началу. Если необходимо обеспечить доступ к массиву как в таймсериях, то для этого используют динамические массивы.
При доступе за пределы массива исполняющая подсистема сгенерирует критическую ошибку и выполнение программы будет остановлено.
Спецификаторы доступа
Спецификаторы доступа указывают компилятору каким образом можно осуществлять доступ к переменным, членам структур или классов. Переменные типа const не могут изменяться во время выполнения программы. Допускается однократная инициализация переменной при ее объявлении.
Пример:
int OnCalculate (const int rates_total, // размер массива price[] const int prev_calculated, // обработано баров на предыдущем вызове const int begin, // откуда начинаются значимые данные const double& price[] // массив для расчета );
Для доступа к членам структур и классов используются следующие спецификаторы:
public – разрешает ничем неограниченный доступ к переменной или методу класса;
protected – разрешает доступ со стороны методов данного класса, а также со стороны методов публично наследуемых классов. Иной доступ невозможен;
private – разрешает доступ к переменным и методам класса только из методов данного класса.
virtual – применим только к методам класса (но не к методам структур) и сообщает компилятору, что данный метод должен быть размещен в таблице виртуальных функций класса.
Классы памяти
Существуют три класса памяти: static, input и extern. Эти модификаторы класса памяти явно указывают компилятору, что соответствующие переменные распределяются в предопределенной области памяти, называемой глобальным пулом. При этом данные модификаторы указывают на особую обработку данных переменных.
Если переменная, объявленная на локальном уровне, не является статической, то распределение памяти под такую переменную производится автоматически на программном стеке. Освобождение памяти, выделенной под не статический массив, производится также автоматически при выходе за пределы области видимости блока, в котором массив объявлен.
Локальные переменные
Переменная, объявленная внутри какой-либо функции, является локальной. Область видимости локальной переменной ограничена пределами функции, внутри которой она объявлена. Локальная переменная может быть проинициализирована при помощи любого выражения. Инициализация локальной переменной производится каждый раз при вызове соответствующей функции. Локальные переменные располагаются во временной области памяти соответствующей функции.
Пример:
int somefunc() { int ret_code=0; ... return(ret_code); }
Область действия (или область видимости) переменной – это часть программы, в которой на переменную можно сослаться. Переменные, объявленные внутри блока (на внутреннем уровне), имеют областью действия блок. Область действия блок начинается объявлением переменной и заканчивается конечной правой фигурной скобкой.
Локальные переменные, объявленные в начале функции, имеют область действия блок так же, как и параметры функции, являющиеся локальными переменными. Любой блок может содержать объявления переменных. Если блоки вложены и идентификатор во внешнем блоке имеет такое же имя, как идентификатор во внутреннем блоке, идентификатор внешнего блока «невидим» (скрыт) до момента завершения работы внутреннего блока.
Пример:
void OnStart() { //--- int i=5; // локальная переменная функции { int i=10; // переменная функции Print("В блоке i = ",i); // результат i = 10; } Print("Вне блока i = ",i); // результат i = 5; }
Это означает, что пока выполняется внутренний блок, он видит значения своих собственных локальных идентификаторов, а не значения идентификаторов с идентичными именами в охватывающем блоке.
Пример:
void OnStart() { //--- int i=5; // локальная переменная функции for(int i=0;i<3;i++) Print("Внутри for i = ",i); Print("Вне блока i = ",i); } /* Результат выполнения Внутри for i = 0 Внутри for i = 1 Внутри for i = 2 Вне блока i = 5 */
Локальные переменные, объявленные как static, имеют областью действия блок, несмотря на то, что они существуют с самого начала выполнения программы.
Стек
Стеком называется упорядоченный набор элементов, в котором размещение новых и удаление существующих происходит с одного конца, называемого вершиной. Порядок обслуживания — это совокупность правил (упорядочение и алгоритм) обслуживания элементов динамической структуры данных. В зависимости от дисциплины обслуживания различают те или иные структуры динамических данных.
Принцип работы стека сравнивают со стопкой листов бумаги: чтобы взять второй сверху, нужно снять верхний.
В стеке реализуется дисциплина обслуживания LIFO:
LAST — последний
INPUT — вошел
FIRST — первый
OUTPUT — вышел
Стек выделяется один на все функции и по умолчанию для индикаторов его размер равен 1Mb. В экспертах и скриптах размером стека можно управлять директивой компилятора #property stacksize (задает размер стека в байтах), для них по умолчанию под стек выделяется 8Mb.
Статические локальные переменные размещаются там же, где и другие статические и глобальные переменные, в специальной области памяти, существующей отдельно от стека. Динамически создаваемые переменные также используют отдельную от стека область памяти.
При каждом вызове функции для внутренних не статических переменных отводится место на стеке. При выходе из функции память становится доступной для повторного использования.
Если из первой функции производится вызов второй функции, то та в свою очередь занимает на стеке необходимый объем под свои переменные из оставшейся стековой памяти. Таким образом при вложенных вызовах функций на стеке будет заниматься память последовательно под каждую функцию. Это может привести к нехватке памяти при очередном вызове функции, такая ситуация называется переполнением стека.
Поэтому для больших локальных данных лучше использовать динамическую память — при входе в функцию память под локальные нужды выделять в системе (new, ArrayResize()), а при выходе из функции производить освобождение памяти (delete, ArrayFree()).
Формальные параметры
Если функция использует аргументы, то в ней должны объявляться переменные, которые будут принимать значения аргументов. Данные переменные называются формальными параметрами функции. Они ведут себя, как любые другие локальные переменные в функции.
Как и с локальными переменными, формальным параметрам можно присваивать значения или использовать их в любых допустимых выражениях MQL5. Даже если эти переменные играют особую роль для некоторых задач по получению значений аргументов, переданных в функцию, то они могут использоваться, как и остальные локальные переменные.
Передаваемые в функцию параметры являются локальными. Областью видимости является блок функции. Формальные параметры должны отличаться по именам от внешних переменных и локальных переменных, определенных внутри функции. В блоке функции формальным параметрам могут быть присвоены некоторые значения. Если же формальный параметр объявлен с модификатором const, то его значение не может быть изменено внутри функции.
Пример:
void my_func(const int & x[], double y, bool z) { if(y>0.0 && !z) Print(x[0]); ... }
Формальные параметры могут быть проинициализированы константами. В этом случае инициализирующее значение считается значением по умолчанию. Параметры, следующие за проинициализированным параметром, должны быть тоже проинициализированы.
Пример:
void my_func(int x, double y = 0.0, bool z = true) { ... }
При вызове такой функции инициализированные параметры можно опускать, вместо них будут подставлены значения по умолчанию.
Пример:
my_func(123, 0.5);
Параметры простых типов передаются по значению, то есть изменения соответствующей локальной переменной такого типа внутри вызываемой функции никак не отразятся в вызывающей функции. Массивы любых типов и данные типа структур всегда передаются по ссылке. Если необходимо запретить изменение массива или содержимого структуры, параметры этих типов должны быть объявлены с ключевым словом const.
Существует возможность передавать по ссылке и параметры простых типов. В этом случае модификация таких параметров внутри вызываемой функции отразится на соответствующих переменных, переданных по ссылке. Для того чтобы указать, что параметр передается по ссылке, после типа данных необходимо поставить модификатор &.
Пример:
void func(int& x, double& y, double & z[]) { double calculated_tp; ... for(int i=0; i<OrdersTotal(); i++) { if(i==ArraySize(z)) break; if(OrderSelect(i)==false) break; z[i]=OrderOpenPrice(); } x=i; y=calculated_tp; }
Параметры, передаваемые по ссылке, нельзя инициализировать значениями по умолчанию.
В функцию нельзя передать больше 64 параметров.
Статические переменные
Класс памяти static определяет статическую переменную. Модификатор static указывается перед типом данных.
Пример:
int somefunc() { static int flag=10; ... return(flag); }
Статическая переменная может быть проинициализирована соответствующей ее типу константой или константным выражением, в отличие от простой локальной переменной, которая может быть проинициализирована любым выражением.
Статические переменные существуют с момента выполнения программы и инициализируются однократно перед вызовом специализированной функции OnInit(). Если начальные значения не указаны, то переменные статического класса памяти принимают нулевые начальные значения.
Локальные переменные, объявленные с ключевым словом static сохраняют свои значения в течение всего времени существования функции. При каждом следующем вызове функции такие локальные переменные содержат те значения, которые они имели при предыдущем вызове.
Любые переменные в блоке, кроме формальных параметров функции, могут быть определены как статические. Если переменная, объявленная на локальном уровне не является статической, то распределение памяти под такую переменную производится автоматически.
Пример:
int Counter() { static int count; count++; if(count%100==0) Print("Функция Counter была вызвана уже ",count," раз"); return count; } void OnStart() { //--- int c=345; for(int i=0;i<1000;i++) { int c=Counter(); } Print("c = ",c); }
Глобальные переменные
Глобальные переменные создаются путем размещения их объявлений вне описания какой-либо функции. Глобальные переменные определяются на том же уровне, что и функции, т. е. не локальны ни в каком блоке.
Пример:
int GlobalFlag=10; // глобальная переменная int OnStart() { ... }
Область видимости глобальных переменных — вся программа, глобальные переменные доступны из всех функций, определенных в программе. Инициализируются нулем, если явно не задано другое начальное значение. Глобальная переменная может быть проинициализирована только соответствующей ее типу константой либо константным выражением.
Инициализация глобальных переменных производится однократно после загрузки программы в память клиентского терминала и перед первой обработкой события Init. Для глобальных переменных, представляющих собой объекты классов, при инициализации вызываются соответствующие конструкторы. В скриптах инициализация глобальных переменных производится перед обработкой события Start.
Замечание: не следует путать переменные, объявленные на глобальном уровне, с глобальными переменными клиентского терминала, доступ к которым осуществляется при помощи функций GlobalVariable…().
Input переменные
Класс памяти input определяет внешнюю переменную. Модификатор input указывается перед типом данных. Изменять значение переменной с модификатором input внутри mql5-программы нельзя, такие переменные доступны только для чтения. Изменять значения input-переменных может только пользователь из окна свойств программы. Внешние переменные всегда переинициализируются непосредственно перед вызовом OnInit().
Пример:
//--- input parameters input int MA_Period=13; input int MA_Shift=0; input ENUM_MA_METHOD MA_Method=MODE_SMMA;
Input переменные определяют входные параметры программы, они доступны из окна свойств программы.
Установка значения для input-параметра
Существует возможность задать иной способ отображения имен входных параметров на закладке «Inputs». Для этого используется строчный комментарий, который должен располагаться после описания входного параметра в той же строке. Таким образом, входным параметрам можно сопоставить более понятные для пользователя имена.
Пример:
//--- input parameters input int InpMAPeriod=13; // Smoothing period input int InpMAShift=0; // Line horizontal shift input ENUM_MA_METHOD InpMAMethod=MODE_SMMA; // Smoothing method
Человечный способ отображения входных параметров
Примечание: Массивы и переменные сложных типов не могут выступать в качестве input-переменных.
Примечание: Длина строчного комментария для Input переменных не может превышать 63 символа.
Передача параметров при вызове пользовательских индикаторов из MQL5-программ
Пользовательские индикаторы вызываются при помощи функции iCustom(). При этом после имени пользовательского индикатора должны идти параметры в точном соответствии с объявлением input-переменных данного пользовательского индикатора. Если параметров указывается меньше, чем объявлено input-переменных в вызываемом пользовательском индикаторе, то недостающие параметры заполняются значениями, указанными при объявлении переменных.
Если в пользовательском индикаторе используется функция OnCalculate первого вида (то есть, индикатор считается на одном массиве данных), то в качестве последнего параметра при вызове такого пользовательского индикатора должно выступать одно из значений ENUM_APPLIED_PRICE либо хэндл другого индикатора. При этом все параметры, соответствующие input-переменным, должны быть явно указаны.
Перечисления в качестве input-параметра
В качестве input-переменных (входных параметров для mql5-программ) можно использовать не только предусмотренные языком MQL5 встроенные перечисления, но и перечисления, заданные пользователем. Например, мы можем создать перечисление dayOfWeek, описывающее дни недели, и использовать input-переменную для указания конкретного дня недели не в виде цифры, а в более привычном для пользователя виде.
Пример:
#property script_show_inputs //--- day of week enum dayOfWeek { S=0, // Sunday M=1, // Monday T=2, // Tuesday W=3, // Wednesday Th=4, // Thursday Fr=5, // Friday, St=6, // Saturday }; //--- input parameters input dayOfWeek swapday=W;
Для того чтобы при запуске скрипта пользователь мог выбрать нужное значение из окна свойств, мы используем команду препроцессора #property script_show_inputs. Запускаем скрипт на исполнение и можем выбрать из списка одно из значений перечислений dayOfWeek. Запускаем скрипт EnumInInput и переходим на закладку «Параметры». По умолчанию, значение параметра swapday (день начисления тройного свопа) является среда (W=3), но мы можем задать любое другое значение и использовать это значение для изменения работы программы.
Пример пользовательского перечисления в качестве input-параметра
Количество возможных значений перечисления ограничено. Поэтому для выбора входного значения используется выпадающий список. В качестве значений, показываемых в списке, используются мнемонические имена членов перечисления. Если же мнемоническому имени сопоставлен комментарий, как это показано в нашем примере, то вместо мнемонического имени используется содержимое комментария.
Каждое значение из перечисления dayOfWeek имеет свое значение от 0 до 6, но в списке параметров будут показаны комментарии, указанные для каждого значения. Это дает дополнительную гибкость для написания программ с понятными описаниями входных параметров.
Переменные с модификатором sinput
Переменные с модификатором input позволяют не только задавать значения внешних параметров при запуске программ, но также играют большую роль при оптимизации торговых стратегий в тестере. Каждая объявленная в эксперте input-переменная, за исключением типа string, может участвовать в оптимизации.
В некоторых случаях бывает необходимо исключить некоторые внешние параметры программы из формирования области всех возможных проходов в тестере. Специально для таких случаев существует модификатор памяти sinput. sinput — это сокращенное написание объявления статической внешней переменной: sinput = static input. То есть такое объявление в коде советника
sinput int layers=6; // Количество слоев
будет эквивалентно полному объявлению
static input int layers=6; // Количество слоев
Переменная, объявленная с модификатором sinput, является входным параметром MQL5-программы, значение этого параметра можно изменять при её запуске. Но при этом данная переменная не участвует в процессе оптимизации входных параметров, то есть не производится перебор её значений при поиске наилучшего набора параметров по заданному критерию.
Отображение sinput-параметра в тестере стратегий
На рисунке показано, что эксперт имеет 5 внешних параметров, из них параметр «Количество слоев» объявлен как sinput и равен 6. Этот параметр не может изменяться в процедуре оптимизации торговой стратегии, для него возможно установить нужное значение, которое и будет использоваться. Поля Старт, Шаг и Стоп для такой переменной не доступны для установки значений.
Таким образом, задав для переменной модификатор sinput, мы запрещаем пользователю оптимизировать данный параметр. Это значит, что в тестере стратегий пользователю терминала становится недоступным задавать для неё начальное и конечное значения для автоматического перебора в указанном диапазоне в процессе оптимизации.
Но при этом есть одно исключение из данного правила – sinput-переменные можно варьировать в задачах оптимизации с помощью функции ParameterSetRange(). Данная функция создана специально для программного управления пространством доступных значений для любых input-переменных, в том числе и объявленных как static input (sinput). Другая функция ParameterGetRange() позволяет при запуске оптимизации (в обработчике OnTesterInit()) получить значения input-переменных и в случае необходимости переопределить шаг изменения и диапазон, в пределах которого будет перебираться значение оптимизируемого параметра.
Таким образом, сочетание модификатора sinput и двух функций по работе с input-параметрами позволяет создавать гибкие правила для задания интервалов оптимизации одних input-переменных в зависимости от значения других input-переменных.
Extern переменные
Ключевое слово extern используется, чтобы объявить идентификаторы переменных как идентификаторы статического класса памяти с глобальным временем жизни. Такие переменные существуют с момента начала выполнения программы и для них память выделяется и инициализируется сразу после начала выполнения программы.
Можно создавать программы, которые состоят из нескольких исходных файлов, для этого используется директива препроцессору #include. Переменные, объявленные как extern с одним и тем же типом и идентификатором, могут существовать в разных исходных файлах одного проекта.
При компиляции всего проекта все extern-переменные с одним и тем же типом и идентификатором ассоциируются с одним участком памяти пула глобальных переменных. Extern-переменные полезны для раздельной компиляции исходных файлов. Extern-переменные можно инициализировать, но только однократно – недопустимо существование нескольких инициализированных extern-переменных одного и того же типа и с одним и тем же идентификатором.
Инициализация переменных
Любая переменная при определении может быть инициализирована. Если не произведена явная инициализация переменной, то значение, хранящееся в данной переменной, может быть каким угодно. Неявная инициализация не производится.
Глобальные и статические переменные могут быть проинициализированы только константой соответствующего типа или константным выражением. Локальные переменные могут быть проинициализированы любым выражением, а не только константой.
Инициализация глобальных и статических переменных производится однократно. Инициализация локальных переменных производится каждый раз при вызове соответствующих функций.
Примеры:
int n = 1; string s = "hello"; double f[] = { 0.0, 0.236, 0.382, 0.5, 0.618, 1.0 }; int a[4][4] = { {1, 1, 1, 1}, {2, 2, 2, 2}, {3, 3, 3, 3}, {4, 4, 4, 4 } }; //--- из тетриса int right[4]={WIDTH_IN_PIXELS+VERT_BORDER,WIDTH_IN_PIXELS+VERT_BORDER, WIDTH_IN_PIXELS+VERT_BORDER,WIDTH_IN_PIXELS+VERT_BORDER}; //--- инициализация всех полей структуры нулевым значением MqlTradeRequest request={0};
Список значений элементов массива должен быть заключен в фигурные скобки. Пропущенные инициализирующие последовательности считаются равными 0. В инициализирующей последовательности должно быть хотя бы одно значение: этим значением инициализируется первый элемент соответствующей структуры или массива, отсутствующие элементы считаются равными нулю.
Если размер инициализируемого массива не указан, то он определяется компилятором, исходя из размера инициализирующей последовательности. Многомерные массивы не могут инициализироваться одномерной последовательностью (последовательностью без дополнительных фигурных скобок), за исключением одного случая – когда указывается всего один инициализирующий элемент (как правило, ноль).
Массивы (в том числе и объявленные на локальном уровне) могут инициализироваться только константами.
Примеры:
struct str3 { int low_part; int high_part; }; struct str10 { str3 s3; double d1[10]; int i3; }; void OnStart() { str10 s10_1={{1,0},{1.0,2.1,3.2,4.4,5.3,6.1,7.8,8.7,9.2,10.0},100}; str10 s10_2={{1,0},{0},100}; str10 s10_3={{1,0},{1.0}}; //--- Print("1. s10_1.d1[5] = ",s10_1.d1[5]); Print("2. s10_2.d1[5] = ",s10_2.d1[5]); Print("3. s10_3.d1[5] = ",s10_3.d1[5]); Print("4. s10_3.d1[0] = ",s10_3.d1[0]); }
Для переменных типа структур допускается частичная инициализация, это же относится и к статическим массивам (с явно заданным размером). Можно проинициализировать один или несколько первых элементов структуры или массива, оставшиеся элементы в таком случае будут проинициализированы нулями.
Область видимости и время жизни переменных
Существует два основных вида области видимости: локальная область видимости и глобальная область видимости.
Переменная, объявленная вне всех функций, помещается в глобальную область видимости. Доступ к таким переменным может осуществляться из любого места программы. Такие переменные располагаются в глобальном пуле памяти, поэтому время их жизни совпадает со временем жизни программы.
Переменная, объявленная внутри блока (часть кода, заключенная в фигурные скобки), принадлежит локальной области видимости. Такая переменная не видна (поэтому и недоступна) за пределами блока, в котором она объявлена. Самый распространенный случай локального объявления – переменная, объявленная внутри функции. Переменная, объявленная локально, располагается на стеке, и время жизни такой переменной совпадает со временем жизни функции.
Так как областью видимости локальной переменной является блок, в котором она объявлена, то существует возможность объявлять переменные с именем, совпадающим с именами переменных, объявленных в других блоках; а также объявленных на более верхних уровнях, вплоть до глобального.
Пример:
void CalculateLWMA(int rates_total,int prev_calculated,int begin,const double &price[]) { int i,limit; static int weightsum=0; double sum=0; //--- if(prev_calculated==0) { limit=MA_Period+begin; //--- set empty value for first limit bars for(i=0; i<limit; i++) LineBuffer[i]=0.0; //--- calculate first visible value double firstValue=0; for(int i=begin; i<limit; i++) { int k=i-begin+1; weightsum+=k; firstValue+=k*price[i]; } firstValue/=(double)weightsum; LineBuffer[limit-1]=firstValue; } else { limit=prev_calculated-1; } for(i=limit;i<rates_total;i++) { sum=0; for(int j=0; j<MA_Period; j++) sum+=(MA_Period-j)*price[i-j]; LineBuffer[i]=sum/weightsum; } //--- }
Обратите внимание на переменную i, объявленную в строке
for(int i=begin; i<limit; i++) { int k=i-begin+1; weightsum+=k; firstValue+=k*price[i]; }
Ее область видимости – только цикл for, за пределами этого цикла действует другая переменная с тем же именем, объявленная в начале функции. Кроме того, в теле цикла объявлена переменная k, областью видимости которой является тело цикла.
Локальные переменные можно объявлять со спецификатором доступа static. В этом случае компилятор располагает такую переменную в глобальном пуле памяти. Поэтому, время жизни статической переменной совпадает со временем жизни программы. При этом область видимости такой переменной ограничивается пределами блока, в котором она объявлена.
Создание и уничтожение объектов
После загрузки на исполнение mql5-программы каждой переменной выделяется память в соответствие с типом переменной. Переменные делятся на два типа по уровню доступа — глобальные переменные и локальные переменные, и по классам памяти: входные параметры mql5-программы, статические и автоматические. Каждая переменная при необходимости инициализируется соответствующим значением. После использования переменная деинициализируется и память, использованная ею, возвращается исполняемой системе MQL5.
Инициализация и деинициализация глобальных переменных
Инициализация глобальных переменных производится автоматически сразу после загрузки mql5-программы и до вызова любой функции. При инициализации производится присвоение начальных значений переменным простых типов и вызывается конструктор для объектов, если он есть. Входные переменные всегда объявляются на глобальном уровне, инициализируются значениями, задаваемыми пользователями в диалоге при запуске mql5-программы.
Несмотря на то, что статические переменные обычно объявляются на локальном уровне, память под эти переменные распределяется заранее, и инициализация производится сразу после загрузки программы, точно так же как и для глобальных переменных.
Порядок инициализации соответствует порядку объявления переменной в программе, а деинициализация производится в обратном порядке перед выгрузкой mql5-программы. Это правило только для тех переменных, которые не были созданы оператором new. Такие переменные создаются и инициализируются автоматически сразу после загрузки, а деинициализируются непосредственно перед выгрузкой программы.
Инициализация и деинициализация локальных переменных
Если переменная, объявленная на локальном уровне, не является статической, то распределение памяти под такую переменную производится автоматически. Локальные переменные, также как и глобальные, инициализируются автоматически в тот момент, когда выполнение программы встречает объявление локальной переменной. Таким образом, порядок инициализации соответствует порядку объявления.
Локальные переменные деинициализируются в конце блока программы, в котором они объявлены, и в порядке, обратном их объявлению. Блок программы – это составной оператор, который может являться частью оператора выбора switch, цикла(for, while, do-while), телом функции или частью оператора if-else.
Инициализация локальных переменных происходит только в тот момент, когда выполнение программы доходит до объявления переменной. Если в процессе выполнения программы блок, в котором объявлена переменная, не был выполнен, то такая переменная не инициализируется.
Инициализация и деинициализация динамически размещаемых объектов
Особый случай представляют из себя указатели объектов, так как объявление указателя не влечет за собой инициализации соответствующего объекта. Динамически размещаемые объекты инициализируются только в момент создания экземпляра класса оператором new. Инициализация объекта предполагает вызов конструктора соответствующего класса. Если соответствующего конструктора в классе нет, то его члены, имеющие простой тип, не будут автоматически проинициализированы; члены типов строка, динамический массив и сложный объект будут автоматически проинициализированы.
Указатели могут быть объявлены на локальном или глобальном уровне и при этом могут быть проинициализированы пустым значением NULL или значением указателя такого же или порожденного типа. Если для указателя, объявленного на локальном уровне, был вызван оператор new, то и оператор delete для этого указателя должен быть выполнен до выхода из этого уровня. В противном случае указатель будет потерян, и объект не сможет быть удален явно.
Все объекты, созданные выражением указатель_объекта=new Имя_Класса, обязательно должны быть впоследствии уничтожены оператором delete(указатель_объекта). Если по каким то причинам такая переменная по окончании работы программы не была уничтожена оператором delete, то об этом будет выведено сообщение в журнал «Эксперты». Можно объявить несколько переменных и всем им присвоить указатель одного объекта.
Если динамически создаваемый объект имеет конструктор, то этот конструктор будет вызван в момент выполнения оператора new. Если объект имеет деструктор, то деструктор будет вызван в момент выполнения оператора delete.
Таким образом, динамически размещаемые объекты создаются только в момент создания оператором new и гарантированно уничтожаются либо оператором delete, либо автоматически исполняющей системой MQL5 в момент выгрузки программы. Порядок объявления указателей динамически создаваемых объектов не влияет на порядок их инициализации. Порядок инициализации и деинициализации полностью контролируется программистом.
Особенности работы с динамической памятью
При работе с динамическими массивами освобожденная память сразу же возвращается в систему.
При создании динамического объекта класса через new, память сначала ищется в пуле памяти классов, с которым работает менеджер памяти, и если в пуле недостаточно памяти, то память запрашивается в системе. При удалении динамического объекта через delete, память, занимаемая объектом, возвращается в пул памяти классов.
Менеджер памяти возвращает память в систему сразу после выхода из функций-обработчиков событий: OnInit(), OnDeinit(), OnStart(), OnTick(), OnCalculate(), OnTimer(), OnTrade(), OnTester(), OnTesterInit(), OnTesterPass(), OnTesterDeinit(), OnChartEvent(), OnBookEvent().
Краткая характеристика переменных
Основные сведения о порядке создания, уничтожении, вызове конструкторов и деструкторов приведены в краткой таблице.
Глобальная автоматическая переменная |
Локальная автоматическая переменная |
Динамически создаваемый объект |
|
Инициализация |
сразу после загрузки mql5-программы |
при достижении в ходе выполнения строки кода, где она объявлена |
при выполнении оператора new |
Порядок инициализации |
в порядке объявления |
в порядке объявления |
не зависит от порядка объявления |
Деинициализация |
перед выгрузкой mql5-программы |
при выходе выполнения из блока объявления |
при выполнении оператора delete или перед выгрузкой mql5-программы |
Порядок деинициализации |
в порядке, обратном инициализации |
в порядке, обратном инициализации |
не зависит от порядка инициализации |
Вызов конструктора |
при загрузке mql5-программы |
при инициализации |
при выполнении оператора new |
Вызов деструктора |
при выгрузке mql5-программы |
при выходе из блока, в котором переменная была инициализирована |
при выполнении оператора delete |
Сообщения об ошибках |
сообщение в журнал «Эксперты» о попытке удаления автоматически созданного объекта |
сообщение в журнал «Эксперты» о попытке удаления автоматически созданного объекта |
сообщение в журнал «Эксперты» о неудаленных динамически созданных объектах при выгрузке mql5-программы |