Является ли ключевое слово override просто проверкой переопределенного виртуального метода?
Насколько я понимаю, введение override
Ключевое слово в C++11 является не чем иным, как проверкой, чтобы убедиться, что реализуемая функция является override
из virtual
функция в базовом классе.
Это оно?
6 ответов
Это действительно идея. Дело в том, что вы четко понимаете, что имеете в виду, так что в противном случае молчаливая ошибка может быть диагностирована:
struct Base
{
virtual int foo() const;
};
struct Derived : Base
{
virtual int foo() // whoops!
{
// ...
}
};
Приведенный выше код компилируется, но это не то, что вы могли иметь в виду (обратите внимание на отсутствующий const
). Если бы вы сказали вместо этого, virtual int foo() override
, тогда вы получите ошибку компилятора, что ваша функция на самом деле ничего не переопределяет.
Цитата из Википедии:
Специальный идентификатор переопределения означает, что компилятор проверит базовый класс (ы), чтобы увидеть, существует ли виртуальная функция с этой точной сигнатурой. И если нет, компилятор выдаст ошибку.
http://en.wikipedia.org/wiki/C%2B%2B11
Редактировать (пытаясь немного улучшить ответ):
Объявление метода как "переопределить" означает, что этот метод предназначен для перезаписи (виртуального) метода в базовом классе. Переопределяющий метод должен иметь ту же сигнатуру (по крайней мере, для входных параметров), что и метод, который он намеревается переписать.
Почему это необходимо? Хорошо, предотвращены следующие два распространенных случая ошибок:
один опечатки типа в новом методе. Компилятор, не подозревая, что намеревается написать предыдущий метод, просто добавляет его в класс как новый метод. Проблема в том, что старый метод все еще там, новый добавляется просто как перегрузка. В этом случае все вызовы старого метода будут функционировать так же, как и раньше, без каких-либо изменений в поведении (что было бы самой целью переписывания).
забывают объявить метод в суперклассе как "виртуальный", но все же пытаются переписать его в подклассе. Хотя это, очевидно, будет принято, поведение будет не совсем таким, как предполагалось: метод не является виртуальным, поэтому доступ через указатели к суперклассу закончится вызовом старого (суперкласс ') метода вместо нового (подкласс') метода.
Добавление "override" однозначно устраняет это: с помощью этого мы сообщаем компилятору, что ожидаются три вещи:
- в суперклассе есть метод с таким же именем
- этот метод в суперклассе объявлен как "виртуальный" (то есть предназначен для переписывания)
- метод в суперклассе имеет ту же сигнатуру (input*), что и метод в подклассе (метод перезаписи)
Если какой-либо из них является ложным, то сообщается об ошибке.
* примечание: выходной параметр иногда имеет другой, но связанный тип. Читайте о ковариантных и контравариантных преобразованиях, если интересно.
Найденное "переопределение" полезно, когда кто-то обновил сигнатуру виртуального метода базового класса, например, добавил необязательный параметр, но забыл обновить сигнатуру метода производного класса. В этом случае методы между базовым и производным классом больше не являются полиморфными отношениями. Без объявления переопределения трудно обнаружить такую ошибку.
Да это так. Это проверка, чтобы убедиться, что никто не пытается переопределить и испортить его через испорченную подпись. Вот страница Wiki, которая объясняет это подробно и имеет короткий иллюстративный пример:
Проект стандарта C++17
Пройдя через все override
попадает в черновик стандарта C++17 N4659, единственная ссылка, которую я могу найти на override
идентификатор:
5 Если виртуальная функция помечена с помощью переопределения virt-спецификатора и не переопределяет функцию-член базового класса, программа имеет неверный формат. [ Пример:
struct B { virtual void f(int); }; struct D : B { virtual void f(long) override; // error: wrong signature overriding B::f virtual void f(int) override; // OK }
- конец примера]
поэтому я думаю, что, возможно, взорвать неправильные программы на самом деле единственный эффект.
Чтобы прояснить все о виртуальном (раз уж я с этим сталкивался неоднократно!).
- предназначен для того, чтобы сообщить производным классам, что функция может быть переопределена
- Нет необходимости использовать в производных классах. Если функция имеет такое же имя / список типов параметров / cv-qual / ref-qual, она будет автоматически использоваться правильно.
- (на самом деле, используя
virtual
в производных классах могут создавать тонкие ошибки, см. ниже)
- - необязательный спецификатор для обнаружения ошибок и кода документа :
- Сообщает компилятору: «Убедитесь, что есть ТОЧНАЯ виртуальная функция, которую я заменяю»
- Избегает создания ДРУГОЙ сигнатуры функции по ошибке, которая могла бы вызвать небольшую ошибку (т.е. 2 немного разные функции, которые должны быть одинаковыми)
- Сообщает кодировщикам, что это переопределяет виртуальную функцию.
- Сообщает компилятору: «Убедитесь, что есть ТОЧНАЯ виртуальная функция, которую я заменяю»
Так дано:
class base
{
public:
virtual int foo(float x);
};
Вот как будут работать некоторые другие переопределения:
// AUTOMATIC virtual function (matches original, no keywords specified)
int foo(float x) { ; }
// Re-specifying "virtual" uselessly (+ see pitfalls below)
virtual int foo(float x) { ; }
// Potential issues: it is unknown if the author intended this to be a
// virtual function or not. Also, if the author DID intend a match but
// made a mistake (e.g. use "int" for the parameter), this will create
// a subtle bug where the wrong function is called with no warning anywhere:
int foo(int x) { ; } // SUBTLE, SILENT BUG! int instead of float param
virtual int foo(int x) { ; } // SUBTLE, SILENT BUG! int instead of float param
// Better approach: use the 'override' identifier to
// make sure the signature matches the original virtual function,
// and documents programmer intent.
int foo(float x) override { ; } // Compiler checks OK + tells coder this is virtual
int foo(int x) override { ; } // COMPILE ERROR, caught subtle bug
virtual int foo(int x) override { ; } // COMPILE ERROR, caught subtle bug
// (and redundant use of "virtual")
Наконец (!)
final
спецификатор может использоваться вместо
override
по тем же причинам, но в случае, если вы не хотите никаких дополнительных переопределений в производных классах.