Реализация чисто виртуальной функции из абстрактного базового класса: имеет ли значение переопределение спецификатора?
Фон
Я только что наткнулся на случай использованияoverride спецификатор , который, насколько я могу судить, кажется избыточным и также без какого-либо конкретного семантического значения, но, возможно, я что-то упускаю, отсюда и этот вопрос. Прежде чем продолжить, я должен отметить, что я пытался найти ответ на него здесь на SO, но ближе всего я получил следующие темы, не совсем отвечающие на мой запрос (возможно, кто-то может указать вопрос и ответ, который на самом деле уже отвечает на мой вопрос). вопрос).
C++ Virtual / Pure Virtual Explained
C++ переопределить чисто виртуальный метод с помощью чисто виртуального метода
Вопрос
Рассмотрим следующий абстрактный класс:
struct Abstract {
virtual ~Abstract() {};
virtual void foo() = 0;
};
Есть ли какая-либо причина использовать спецификатор
override при реализации foo() в неабстрактном классе, производном непосредственно от Abstract (как в DerivedB ниже)? Т. е., когда реализация foo() уже требуется для того, чтобы производный класс был не абстрактный (и не перекрывающий ничего)?/* "common" derived class implementation, in my personal experience
(include virtual keyword for semantics) */
struct DerivedA : public Abstract {
virtual void foo() { std::cout << "A foo" << std::endl; }
};
/* is there any reason for having the override specifier here? */
struct DerivedB : public Abstract {
virtual void foo() override { std::cout << "B foo" << std::endl; }
};
4 ответов:
Я не большой поклонник
override, но, предполагая, что это то, что вы находите полезным в целом, то да, ставитьoverrideна виртуальную функцию, которая переопределяет чисто виртуальные функции, полезно. Рассмотрим этот довольно надуманный пример:Теперь предположим, что сопровождающийstruct Base { virtual void f() = 0; }; struct Derived : Base { virtual void f(); virtual void f(int); };Base(возможно, даже ваше будущее "я") изменяетBase, чтобы выглядеть следующим образом:struct Base { virtual void f(int) = 0; };Теперь поведение
Derivedтихо изменилось. С помощьюoverrideкомпилятор сообщит об ошибке.
С технической точки зрения обе версии являются синтаксически правильными и законными. Спецификатор
overrideв основном предназначен для предотвращения непредвиденного поведения. Компилятор выдаст ошибку, как только он встретит функцию-член, помеченную какoverride, которая фактически не переопределяет виртуальную функцию. Это может произойти, если по какой-либо причине вы измените сигнатуру функции виртуального базового класса. Рассмотрим следующий пример:class Abstract { virtual void foo() { ...} }; class Derived : public Abstract { void foo() override { ... } };Теперь, если подпись
Abstract::fooизменена, скажем toclass Abstract { virtual void foo(int bar) { ...} };Компилятор выдаст ошибку в
Derived::foo, поскольку он больше не переопределяет функциюAbstract, что было бы невозможно без квалификатораoverride. Это поможет вам лучше поддерживать ваш код. Однако в вашем конкретном случае (т. е. с чисто виртуальными объявлениями) также будет выдана ошибка. Так что использованиеoverrideв основном считается "хорошей практикой", я полагаю. Подробнее по этой теме: http://en.cppreference.com/w/cpp/language/override
В случае чисто виртуальных функций и компиляций не очень. Вы получите ошибку в любом случае (кроме как в Примере от Пита)
Но сообщение об ошибке может быть более читаемым, если вы получаете ошибку типа "ваша функция ничего не переопределяет " по сравнению с более поздним "не может создать экземпляр абстрактного класса "
Еще одно преимущество будет заключаться в том, что при чтении объявления вы знаете, что это производный метод от базового класса.
И это хорошо практика, чтобы просто привыкнуть объявлять все переопределенные методы с
override. Так зачем же делать разницу здесь и иметь непоследовательный стиль.Что касается Почему хорошо, чтобы все переопределенные методы были объявлены
override:Представьте, что у вас есть
class A { virtual void Foo(); }; class B: public A { virtual void Foo() override; };И затем вы меняете
Fooнаconstфункцию вA. Безoverrideэто будет компилироваться, но когда вы вызываетеA->foo()и это B объектB->foo()не будет вызван без каких-либо указаний что-то изменилось там. Сoverrideтобой получите ошибку здесь.
Главным преимуществом
overrideздесь было бы поощрение ремонтопригодности. Рассмотрим ниже:class Foo { public: virtual void foo() = 0; }; class Derived : public Foo { public: //.... virtual void foo(double x) override { //This throws error } };Как вы можете видеть выше, компилятор выдаст ошибку, если вы скомпилировали выше. Компилятор будет жаловаться на то, что функция не имеет такой же сигнатуры. Без ключевого слова
overrideрезультат был бы иным.
Comments