final 是否意味着覆盖?

Does final imply override?

据我了解,override 关键字表示给定的声明实现了基 virtual 方法,如果找不到匹配的基方法,编译应该会失败。

我对 final 关键字的理解是它告诉编译器任何 class 都不应覆盖此 virtual 函数。

所以override final是多余的吗? It seems to compile fineoverride final 传达了哪些 final 没有传达的信息?这种组合的用例是什么?

final 并不一定意味着函数被覆盖。在继承层次结构中的 first 声明中将虚函数声明为 final 是完全有效的(如果值有点可疑)。

我能想到创建一个虚拟且立即结束的函数的一个原因是,如果您想防止派生的class给出相同的名称和参数不同的意思。

没有final并不一定意味着override。事实上,您可以声明一个 virtual 函数,然后立即声明 final see herefinal 关键字简单地声明没有派生的 class 可以创建此函数的覆盖。

override 关键字很重要,因为它强制您确实实际上覆盖了一个虚函数(而不是声明一个新的不相关的函数)。参见 this post regarding override

长话短说,它们各自服务于自己的特定目的,通常两者都使用是正确的。

final 一开始不需要函数覆盖任何东西。它的效果在[class.virtual]/4中定义为

If a virtual function f in some class B is marked with the virt-specifier final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed.

就是这样。现在 override final 就意味着
„此函数覆盖基数 class 一个 (override) 并且不能自行覆盖 (final)。“
final 本身会施加较弱的要求。 overridefinal 具有独立的行为。


请注意 final 只能用于虚函数 - [class.mem]/8

A virt-specifier-seq shall appear only in the declaration of a virtual member function (10.3).

因此声明

void foo() final;

实际上与

相同
virtual void foo() final override;

因为两者都需要 foo 来覆盖某些东西 - 第二个声明使用 override,第一个声明有效当且仅当 foo 是隐式虚拟的,即 foo 覆盖基 class 中名为 foo 的虚函数时,这使得派生函数中的 foo 自动成为虚函数。 因此override 在出现 final 而不是 virtual 的声明中是多余的。
尽管如此,后一种声明表达的意图更加清晰,绝对应该是首选。

以下代码(带有 final 说明符)编译。但是当 final 被替换为 override final 时编译失败。因此 override finalfinal.

传达更多信息(并防止编译)
class Base
{
public:
    virtual ~Base() {}
};

class Derived : public Base
{
public:
    virtual void foo() final
    {
        std::cout << "in Derived foo\n";
    }
};

本质上,override final 表示此方法不能在任何派生 class 中重写此方法重写基 class 中的虚拟方法]. final 单独没有指定基础 class 覆盖部分。

(赶时间的可以跳到最后看结论)

overridefinal都只能出现在虚函数的声明中。并且两个关键字都可以在同一个函数声明中使用,但是同时使用它们是否有用要视情况而定。

以如下代码为例:

#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()
  1. 比较f1()f6()。我们知道 overridefinal 在语义上是独立的。

    • override 表示该函数正在覆盖其基础 class 中的虚函数。参见 f1()f3()
    • final 表示该函数不能被派生的 class 覆盖。 (但函数本身不需要重写基础 class 虚函数。)参见 f6()f4().
  2. 比较f2()f3()。我们知道,如果一个成员函数声明时不带virtual而带final,就意味着它已经覆盖了基class中的一个虚函数。在这种情况下,关键字 override 是多余的。

  3. 比较f4()f5()。我们知道,如果一个成员函数是用virtual声明的,如果它不是继承层次中第第一个虚函数,那么我们应该用override来指定覆盖关系。否则,我们可能会不小心在 derived class.

  4. 中添加新的虚函数
  5. 比较f1()f7()。我们知道,任何成员函数,不仅仅是虚函数,都可以在派生class中被覆盖。 virtual 指定的是 多态性 ,这意味着 运行 哪个函数的决定延迟到 运行 时间而不是编译时间。 (这在实践中应该避免。)

  6. 比较f7()f8()。我们知道我们甚至可以重写一个基础 class 函数并使它成为一个新的虚拟函数。 (这意味着从 D 派生的 class 的任何成员函数 f8() 都是虚函数。)(这在实践中也应该避免。)

  7. 比较f7()f9()。我们知道 override 可以帮助我们在 derived class 中重写虚函数而忘记在 base class 中添加关键字 virtual 时找到错误。

总结,我个人认为的最佳实践是:

  • 在基class;[=87 中的第一个 虚函数声明中使用virtual =]
  • 总是使用 override 在派生 class 中指定覆盖虚函数,除非还指定了 final