- Инкапсуляция и расширяемость типов
- Наследование
- Полиморфизм
- Перегрузка
- Виртуальные функции
- Статические члены класса
- Шаблоны функций
- Шаблоны классов
- Абстрактные классы
Виртуальные функции
Ключевое слово virtual служит спецификатором функции, который обеспечивает механизм для динамического выбора на этапе выполнения подходящей функции-члена среди функций базового и производного классов, структуры не могут иметь виртуальных функций. Оно может применяться для изменения объявлений только функций-членов.
Виртуальная функция, как и обычная функция, должна иметь исполняемое тело. При вызове семантика ее точно такая же, как и у остальных функций.
Виртуальная функция может замещаться в производном классе. Выбор того, какое определение функции вызвать для виртуальной функции, происходит динамически (на этапе выполнения). Типичный случай – когда базовый класс содержит виртуальную функцию, а производные классы имеют свои версии этой функции.
Указатель на базовый класс может указывать либо на объект базового класса, либо на объект производного класса. Выбор вызываемой функции-члена будет произведен на этапе выполнения и будет зависеть от типа объекта, а не от типа указателя. При отсутствии члена производного типа по умолчанию используется виртуальная функция базового класса.
Деструкторы всегда являются виртуальными, независимо от того, объявлены они с ключевым слово virtual или нет.
Рассмотрим использование виртуальных функций на примере программы MT5_Tetris.mq5. Во включаемом файле MT5_TetrisShape.mqh определен базовый класс CTetrisShape с виртуальной функцией Draw (рисовать).
//+------------------------------------------------------------------+
|
Далее для каждого производного класса эта функция реализована в соответствии с особенностями класса-потомка. Например, первая фигура CTetrisShape1 имеет свою реализацию функции Draw():
class CTetrisShape1 : public CTetrisShape
|
Фигура квадрат описана классом CTetrisShape6 и имеет собственную реализацию метода Draw():
class CTetrisShape6 : public CTetrisShape
|
В зависимости от того, объект какого класса создан, вызывается виртуальная функция того или иного производного класса.
void CTetrisField::NewShape()
|
Модификатор override #
Модификатор override означает, что объявляемая функция обязательно должна переопределить метод родительского класса. Использование этого модификатора позволяет избежать ошибок при переопределении, таких как случайное изменение сигнатуры метода. Например, в базовом классе определен метод func, принимающий в качестве аргумента переменную типа int:
class CFoo |
Далее метод переопределяется в наследуемом классе:
class CBar : public CFoo |
Но по ошибке тип аргумента изменяется с int на short. Фактически, в этом случае уже происходит не переопределение, а перегрузка метода. Действуя в соответствии с алгоритмом определения перегруженной функции, в определенных ситуациях компилятор может выбрать метод, определенный в базовом классе, вместо переопределенного метода.
Чтобы избежать подобных ошибок, к переопределяемому методу следует явно добавлять модификатор override.
class CBar : public CFoo |
Если при переопределении будет изменена сигнатура метода, компилятор не сможет найти в родительском классе метод с точной такой же сигнатурой и выдаст ошибку компиляции:
'CBar::func' method is declared with 'override' specifier but does not override any base class method |
Модификатор final #
Модификатор final действует наоборот — он запрещает переопределение метода в классах-наследниках. Если реализация метода самодостаточна и полностью завершена, объявите его с модификатором final, чтобы он гарантированно не был изменен в последующем.
class CFoo |
При попытке переопределения метода с модификатором final, как показано в примере выше, компилятор выдаст ошибку:
'CFoo::func' method declared as 'final' cannot be overridden by 'CBar::func' |
Смотри также