Подтвердить что ты не робот

В конечном итоге подразумевается переопределение?

Как я понимаю, ключевое слово override заявляет, что данное объявление реализует базовый метод virtual, и компиляция должна завершиться неудачно, если не найдено соответствующего базового метода.

Мое понимание ключевого слова final заключается в том, что он сообщает компилятору, что ни один класс не будет переопределять эту функцию virtual.

Итак, override final избыточно? Кажется, он компилируется отлично. Какую информацию передает override final, что final нет? Каков прецедент для такой комбинации?

4b9b3361

Ответ 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 самостоятельно не указывает основную часть переопределения класса.