- Основы ООП: абстракция
- Основы ООП: инкапусляция
- Основы ООП: наследование
- Основы ООП: полиморфизм
- Основы ООП: композиция (дизайн)
- Определение класса
- Права доступа
- Конструкторы: по умолчанию, параметрический, копирования
- Деструкторы
- Ссылка на себя: this
- Наследование
- Динамическое создание объектов: new и delete
- Указатели
- Виртуальные методы (virtual и override)
- Статические члены
- Вложенные типы, пространства имен и оператор контекста '::'
- Разнесение объявления и определения класса
- Абстрактные классы и интерфейсы
- Перегрузка операторов
- Приведение объектных типов: dynamic_cast и указатель void *
- Указатели, ссылки и const
- Управление наследованием: final и delete
Указатели, ссылки и const
После изучения встроенных и объектных типов, понятий ссылка и указатель, вероятно, имеет смысл сделать сравнение всех доступных модификаций типов.
Ссылки в MQL5 используются только при описании параметров функций и методов. Более того, параметры объектных типов обязательно должны передаваться по ссылке.
void function(ClassOrStruct &object) { } // можно
|
Здесь ClassOrStruct — имя класса или структуры.
В качестве аргумента для параметра типа ссылки допустимо передавать только переменные (LValue), но не константы или временные значения, полученные в результате вычисления выражения.
Создать переменную ссылочного типа или вернуть ссылку из функции нельзя.
ClassOrStruct &function(void) { return Class(); } // нельзя
|
Указатели в MQL5 доступны только для объектов классов. Указатели на переменные встроенных типов или структур не поддерживаются.
Вы можете объявить переменную или параметр функции типа указатель на объект, а также вернуть указатель на объект из функции.
ClassOrStruct *pointer; // можно
|
Однако нельзя возвращать указатель на локальный автоматический объект, так как последний будет освобожден при выходе из функции, и указатель станет недействительным.
Если функция вернула указатель на объект, распределенный динамически внутри функции с помощью new, то вызывающий код должен "не забыть" освободить указатель с помощью delete.
Указатель, в отличие от ссылки, может быть равен NULL. Параметры-указатели могут иметь значение по умолчанию, ссылки — нет (ошибка "reference cannot be initialized").
void function(ClassOrStruct *object = NULL) { } // можно
|
Ссылки и указатели могут быть совмещены в описании параметра. Так функция может принимать ссылку на указатель: тогда изменения указателя в функции станут доступны в вызывающем коде. В частности, подобным образом можно реализовать фабричную функцию, которая ответственна за создание объектов.
void createObject(ClassName *&ref)
|
Правда, для возврата из функции одного указателя обычно принято использовать оператор return, так что данный пример — несколько искусственный. Однако в тех случаях, когда передать наружу необходимо массив указателей, ссылка на него в параметре становится предпочтительным вариантом. Например, в некоторых классах стандартной библиотеки для работы с классами-контейнерами типа карт с парами [ключ,значение] (MQL5/Include/Generic/SortedMap.mqh, MQL5/Include/Generic/HashMap.mqh) имеются методы CopyTo для получения массивов с элементами CKeyValuePair.
int CopyTo(CKeyValuePair<TKey,TValue> *&dst_array[], const int dst_start = 0); |
Тип параметра dst_array может показаться незнакомым: это шаблон класса. Мы изучим шаблоны в следующей главе. Здесь для нас пока важно лишь то, что это ссылка на массив указателей.
Особое поведение для всех типов накладывает модификатор const. В отношении встроенных типов он был рассмотрен в разделе Переменные-константы. Для объектных типов есть свои особенности.
Если переменная или параметр функции описан как указатель или ссылка на объект (ссылка — только в случае параметра), то наличие у них модификатора const ограничивает набор методов и свойств, к которым можно обращаться, только теми, что также имеют модификатор const. Иными словами, через константные ссылки и указатели доступны только константные свойства.
При попытке вызвать неконстантный метод или изменить неконстантное поле компилятор выдаст ошибку: "вызов неконстантного метода для константного объекта" ("call non-const method for constant object") или "константу нельзя изменить" ("constant cannot be modified").
Неконстантный параметр-указатель может принять любой аргумент (как константу, так и неконстанту).
Следует иметь в виду, что в описании указателя можно ставить два модификатора const: один будет относиться к объекту, а второй — к указателю:
- Class *pointer — указатель на объект; объект и указатель работают без ограничений;
- const Class *pointer — указатель на константный объект; для объекта доступны только константные методы и чтение свойств, но указатель можно изменить (присвоить ему адрес другого объекта);
- const Class * const pointer — константный указатель на константный объект; для объекта доступны только константные методы и чтение свойств; указатель менять нельзя;
- Class * const pointer — константный указатель на объект; указатель менять нельзя, а свойства объекта — можно.
Рассмотрим в качестве примера следующий класс Counter (CounterConstPtr.mq5).
class Counter
|
В нем искусственно сделана публичной переменная counter. Также в классе имеется два метода, один из которых константный (clone), а второй — нет (increment). Напомним, что константный метод не имеет права менять поля объекта.
Следующая функция с параметром типа Counter *ptr может вызывать все методы класса и менять его поля.
void functionVolatile(Counter *ptr)
|
Следующая функция с параметром const Counter *ptr вызовет пару ошибок.
void functionConst(const Counter *ptr)
|
Наконец, следующая функция с параметром const Counter * const ptr позволяет еще меньше.
void functionConstConst(const Counter * const ptr)
|
В функции OnStart, где мы описали два объекта Counter (один константный, а другой нет), можно вызывать эти функции с некоторыми исключениями:
void OnStart()
|
Во-первых, обратим внимание, что для переменных точно также генерируется ошибка при попытке вызвать константный метод increment для неконстантного объекта.
Во-вторых, в функцию functionVolatile нельзя передать constCounter — получаем ошибку "нельзя конвертировать константный указатель в неконстантный" ("cannot convert from const pointer to nonconst pointer").
Однако обе ошибки можно обойти с помощью явного приведения типа без модификатора const. Хотя так делать не рекомендуется.