- Основы ООП: абстракция
- Основы ООП: инкапусляция
- Основы ООП: наследование
- Основы ООП: полиморфизм
- Основы ООП: композиция (дизайн)
- Определение класса
- Права доступа
- Конструкторы: по умолчанию, параметрический, копирования
- Деструкторы
- Ссылка на себя: this
- Наследование
- Динамическое создание объектов: new и delete
- Указатели
- Виртуальные методы (virtual и override)
- Статические члены
- Вложенные типы, пространства имен и оператор контекста '::'
- Разнесение объявления и определения класса
- Абстрактные классы и интерфейсы
- Перегрузка операторов
- Приведение объектных типов: dynamic_cast и указатель void *
- Указатели, ссылки и const
- Управление наследованием: final и delete
Динамическое создание объектов: new и delete
До сих пор мы пробовали создавать только автоматические объекты, то есть локальные переменные внутри OnStart. Объект, описанный в глобальном контексте (вне функции OnStart или какой-либо другой), также был бы автоматически создан (в момент загрузки скрипта) и удален (в момент выгрузки скрипта).
Кроме этих двух режимов мы затронули возможность описать поле объектного типа (в нашем примере это структура Pair, использованная для поля coordinates внутри объекта Shape). Все такие объекты тоже являются автоматическими: они создаются для нас компилятором в конструкторе объекта-"хозяина" и удаляются в его деструкторе.
Однако в программах далеко не всегда удается обойтись только автоматическими объектами. В случае программы рисования нам потребуется создавать фигуры по запросу пользователя. Более того, фигуры нужно будет хранить в массиве, а для этого автоматические объекты должны были бы иметь конструктор по умолчанию (что в нашем случае не так).
Для подобных ситуаций MQL5 предоставляет возможность динамического создания и удаления объектов. Создание выполняется с помощью оператора new, а удаление — с помощью оператора delete.
Оператор new
После ключевого слова new следует имя требуемого класса и, в круглых скобках, перечень аргументов для вызова любого из существующих конструкторов. Выполнение оператора new приводит к созданию экземпляра класса.
Оператор new возвращает значение особого типа — указатель на объект. Для описания переменной такого типа следует после имени класса добавить символ звездочки '*'. Например:
Rectangle *pr = new Rectangle(100, 200, 50, 75, clrBlue); |
Здесь переменная pr имеет тип указателя на объект класса Rectangle. Указатели будут более подробно рассмотрены в отдельном разделе.
Важно отметить, что описание самой переменной типа указатель на объект не выделяет память под объект и не вызывает его конструктор. Конечно, указатель занимает место — 8 байт, но по сути это беззнаковое целое ulong, которое система интерпретирует особым образом.
С указателем можно работать точно также, как с объектом, то есть вызывать через оператор разыменования доступные методы и обращаться к полям.
Print(pr.toString()); |
Переменная-указатель, которой еще не присвоен дескриптор динамического объекта (например, если вызов оператора new производится не в момент инициализации новой переменной, а перенесен на какие-то более поздние строки исходного кода), содержит специальный нулевой указатель: он обозначается как NULL (чтобы отличать его от чисел), но фактически равен 0.
Оператор delete
Полученные через new указатели следует освобождать по завершении алгоритма с помощью оператора delete. Например:
delete pr; |
Если этого не сделать, экземпляр, выделенный оператором new, останется в памяти. Если таким образом будут создаваться все новые и новые объекты, и потом не удаляться, когда в них отпала необходимость, это приведет к лишнему расходу памяти. Оставшиеся неосвобожденные динамические объекты вызывают вывод предупреждений по завершении программы. Например, если не удалить указатель pr, получим в журнале после выгрузки скрипта примерно следующее:
1 undeleted objects left
|
Терминал сообщает, сколько объектов и какого класса было забыто программистом, а также какой объем памяти они занимали.
После того как для указателя вызван оператор delete, указатель становится недействительным, так как объект уже не существует. Последующая попытка обратиться к его свойствам вызывает ошибку времени исполнения "Обращение по неверному указателю":
Critical error while running script 'shapes (EURUSD,H1)'.
|
Работа MQL-программы при этом прерывается.
Это, однако, не значит, что ту же самую переменную-указатель больше нельзя использовать. Достаточно присвоить ей указатель на другой вновь созданный экземпляр объекта.
MQL5 имеет встроенную функцию, которая позволяет проверить правильность указателя в переменной — CheckPointer:
ENUM_POINTER_TYPE CheckPointer(object *pointer); |
Она принимает один параметр типа указатель на класс и возвращает значение из перечисления ENUM_POINTER_TYPE:
- POINTER_INVALID — неверный указатель;
- POINTER_DYNAMIC — действующий указатель на динамический объект;
- POINTER_AUTOMATIC — действующий указатель на автоматический объект.
Выполнять оператор delete имеет смысл только для указателя, для которого функция вернула POINTER_DYNAMIC. Для автоматического объекта он не будет иметь эффекта (такие объекты удаляются автоматически при возврате управления из блока кода, в котором определена переменная).
Следующий макрос упрощает и обеспечивает корректность очистки указателя:
#define FREE(P) if(CheckPointer(P) == POINTER_DYNAMIC) delete (P) |
Необходимость явным образом "подчищать за собой хвосты" — неизбежная плата за ту гибкость, которую предоставляют динамические объекты и указатели.