Classes and class hierarchies
C.128
Виртуальные функции должны указывать ровно один из `virtual`, `override` или `final`
Причина
Читаемость. Обнаружение ошибок. Написание явных virtual, override или final самодокументируется и позволяет компилятору перехватывать несоответствия типов и/или имён между базовым и производным классами. Однако написание более одного из этих трёх и избыточно, и потенциальный источник ошибок.
Это простая и ясно:
virtualозначает ровно и только "это новая виртуальная функция."overrideозначает ровно и только "это переопределение без финала."finalозначает ровно и только "это финальное переопределение."
Плохой пример
struct B {
void f1(int);
virtual void f2(int) const;
virtual void f3(int);
// ...
};
struct D : B {
void f1(int); // плохо (надеяться на предупреждение): D::f1() скрывает B::f1()
void f2(int) const; // плохо (но обычно и допустимо): нет явного переопределения
void f3(double); // плохо (надеяться на предупреждение): D::f3() скрывает B::f3()
// ...
};
Хороший пример
struct Better : B {
void f1(int) override; // ошибка (перехвачена): Better::f1() скрывает B::f1()
void f2(int) const override;
void f3(double) override; // ошибка (перехвачена): Better::f3() скрывает B::f3()
// ...
};
Обсуждение
Мы хотим исключить два частных класса ошибок:
- неявная виртуальность: программист хотел, чтобы функция была неявно виртуальной, и она является таковой (но читатели кода не могут этого сказать); или программист хотел, чтобы функция была неявно виртуальной, но она ею не является (например, из-за тонкого несоответствия списка параметров); или программист не хотел, чтобы функция была виртуальной, но она является таковой (потому что она случайно имеет ту же сигнатуру, что и виртуальная в базовом классе)
- неявное переопределение: программист хотел, чтобы функция была неявно переопределением, и она является таковым (но читатели кода не могут этого сказать); или программист хотел, чтобы функция была неявно переопределением, но она ею не является (например, из-за тонкого несоответствия списка параметров); или программист не хотел, чтобы функция была переопределением, но она является таковым (потому что она случайно имеет ту же сигнатуру, что и виртуальная в базовом классе — заметьте, эта проблема возникает независимо от того, явно ли функция объявлена виртуальной, потому что программист мог намереваться создать либо новую виртуальную функцию, либо новую невиртуальную функцию)
Примечание: На классе, определённом как final, каждая отдельная виртуальная функция должна использовать либо override, либо final; в этом случае нет семантического различия.
Примечание: Используйте final на функциях редко. Это не обязательно ведёт к оптимизации и препятствует дальнейшему переопределению.
Применение
- Сравните имена виртуальных функций в базовых и производных классах и обозначьте использование одного имени, которое не переопределяет.
- Обозначьте переопределения ни с
overrideни сfinal. - Обозначьте объявления функций, которые используют более одного из
virtual,overrideиfinal.