В конечном итоге подразумевается переопределение?
Как я понимаю, ключевое слово override
заявляет, что данное объявление реализует базовый метод virtual
, и компиляция должна завершиться неудачно, если не найдено соответствующего базового метода.
Мое понимание ключевого слова final
заключается в том, что он сообщает компилятору, что ни один класс не будет переопределять эту функцию virtual
.
Итак, override final
избыточно? Кажется, он компилируется отлично. Какую информацию передает override final
, что final
нет? Каков прецедент для такой комбинации?
Ответы
Ответ 1
final
не требует, чтобы функция переопределяла что-либо в первую очередь. Его эффект определен в [class.virtual]/4 как
Если виртуальная функция f
в некотором классе B
отмечена virt-specifier final
и в классе D
, полученном из B
, функция D::f
переопределяет B::f
, программа плохо сформирована.
Что это. Теперь override final
будет просто означать
"Эта функция переопределяет базовый класс (override
) и не может быть переопределена (final
)".
final
сам по себе наложил бы более слабое требование.
override
и final
имеют независимое поведение.
Обратите внимание, что final
может использоваться только для виртуальных функций, но - [class.mem]/8
Спецификатор virt-seq появляется только в объявлении виртуальная функция-член (10.3).
Следовательно, декларация
void foo() final;
Является фактически тем же самым, что и
virtual void foo() final override;
Поскольку оба требуют foo
переопределить что-то - второе объявление с помощью override
, а первое - действительным тогда и только тогда, когда foo
неявно виртуально, т.е. , когда foo
является переопределяющим виртуальную функцию, называемую foo
в базовом классе, которая делает foo
в производном автоматически виртуальным. Таким образом, override
будет излишним в объявлениях, где встречается final
, но не virtual
.
Тем не менее, последнее выражение выражает намерение намного яснее и, безусловно, должно быть предпочтительным.
Ответ 2
final
необязательно означает, что функция переопределена. Это совершенно справедливо (если несколько сомнительное значение) объявить виртуальную функцию как final
в ее первом объявлении в иерархии наследования.
Одной из причин, по которым я могу думать о создании виртуальной и немедленной окончательной функции, является то, что вы хотите, чтобы производный класс не придавал одинаковому имени и параметрам другое значение.
Ответ 3
(Пропустите в конец, чтобы увидеть вывод, если вы спешите.)
Оба override
и final
могут отображаться только в объявлении в виртуальной функции. И оба ключевых слова могут использоваться в одном объявлении функции, но полезно ли использовать их, оба зависят от ситуаций.
В качестве примера возьмем следующий код:
#include <iostream>
using std::cout; using std::endl;
struct B {
virtual void f1() { cout << "B::f1() "; }
virtual void f2() { cout << "B::f2() "; }
virtual void f3() { cout << "B::f3() "; }
virtual void f6() final { cout << "B::f6() "; }
void f7() { cout << "B::f7() "; }
void f8() { cout << "B::f8() "; }
void f9() { cout << "B::f9() "; }
};
struct D : B {
void f1() override { cout << "D::f1() "; }
void f2() final { cout << "D::f2() "; }
void f3() override final { cout << "D::f3() "; } // need not have override
// should have override, otherwise add new virtual function
virtual void f4() final { cout << "D::f4() "; }
//virtual void f5() override final; // Error, no virtual function in base class
//void f6(); // Error, override a final virtual function
void f7() { cout << "D::f7() "; }
virtual void f8() { cout << "D::f8() "; }
//void f9() override; // Error, override a nonvirtual function
};
int main() {
B b; D d;
B *bp = &b, *bd = &d; D *dp = &d;
bp->f1(); bp->f2(); bp->f3(); bp->f6(); bp->f7(); bp->f8(); bp->f9(); cout << endl;
bd->f1(); bd->f2(); bd->f3(); bd->f6(); bd->f7(); bd->f8(); bd->f9(); cout << endl;
dp->f1(); dp->f2(); dp->f3(); dp->f6(); dp->f7(); dp->f8(); dp->f9(); cout << endl;
return 0;
}
Выходной сигнал
B::f1() B::f2() B::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() D::f7() D::f8() B::f9()
-
Сравните f1()
и f6()
. Мы знаем, что override
и final
не зависит от семантики.
-
override
означает, что функция переопределяет виртуальную функцию в базовом классе. См. f1()
и f3()
.
-
final
означает, что функция не может быть переопределена его производным классом. (Но сама функция не должна переопределять виртуальную функцию базового класса.) См. f6()
и f4()
.
-
Сравните f2()
и f3()
. Мы знаем, что если функция-член объявлена без virtual
и final
, это означает, что она уже переопределяет виртуальную функцию в базовом классе. В этом случае ключевое слово override
является избыточным.
-
Сравните f4()
и f5()
. Мы знаем, что если функция-член объявлена с помощью virtual
, и если она не является первой виртуальной функцией в иерархии наследования, мы должны использовать override
для указания отношения переопределения. В противном случае мы можем случайно добавить новую виртуальную функцию в производный класс.
-
Сравните f1()
и f7()
. Мы знаем, что любая функция-член, а не только виртуальная, может быть переопределена в производном классе. Что означает virtual
- это полиморфизм, что означает, что решение о том, какая функция запускаться, задерживается до времени выполнения, а не времени компиляции. (Этого следует избегать на практике.)
-
Сравните f7()
и f8()
. Мы знаем, что мы можем даже переопределить функцию базового класса и сделать ее новой виртуальной. (Это означает, что любая функция-член f8()
класса, полученного из D
, будет виртуальной.) (Этого также следует избегать на практике.)
-
Сравните f7()
и f9()
. Мы знаем, что override
может помочь нам найти ошибку, если мы хотим переопределить виртуальную функцию в производном классе, забыв добавить ключевое слово virtual
в базовый класс.
В заключение лучшая практика в моем собственном представлении:
- использовать
virtual
только в объявлении первой виртуальной функции в базовом классе;
- всегда используйте
override
для указания переопределения виртуальной функции в производном классе, если не указано final
.
Ответ 4
Нет final
не обязательно означает override
. Фактически, вы можете объявить функцию virtual
, которую вы сразу объявляете final
см. Здесь. Ключевое слово final
просто указывает, что никакой производный class
не может создать переопределение этой функции.
Ключевое слово override
важно, поскольку оно обеспечивает, что вы действительно фактически переопределяете виртуальную функцию (вместо объявления новой несвязанной). Смотрите этот пост относительно override
Короче говоря, каждый из них выполняет свою особую цель, и часто бывает правильно использовать оба.
Ответ 5
Скомпилирован следующий код (с спецификатором final
). Но компиляция не выполняется, если final
заменяется на override final
. Таким образом, override final
передает больше информации (и предотвращает компиляцию), чем просто final
.
class Base
{
public:
virtual ~Base() {}
};
class Derived : public Base
{
public:
virtual void foo() final
{
std::cout << "in Derived foo\n";
}
};
По существу, override final
говорит, что этот метод нельзя переопределить в любом производном классе, и этот метод переопределяет виртуальный метод в базовом классе. final
самостоятельно не указывает основную часть переопределения класса.