final是否意味着覆盖

Does final imply override?

本文关键字:覆盖 意味着 是否 final      更新时间:2023-10-16

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

我对final关键字的理解是,它"告诉"编译器,任何类都不能覆盖这个virtual函数。

那么override final是多余的吗?它似乎编译得很好。override final传达了哪些final没有传达的信息?这种组合的用例是什么?

final一开始不需要函数覆盖任何内容。其影响在[class.virtual]/4中定义为

如果某个类CCD_ 10中的虚拟函数CCD_virt说明符final和从B派生的类D中的函数D::f覆盖B::f,则程序格式不正确。

就是这样。现在override final的意思就是
·此函数覆盖基类1(override),并且不能被自身覆盖(final)。"
final本身将施加较弱的要求。CCD_ 20和CCD_。


注意,final只能用于通过-[class.mem]/8 的虚拟函数

virt说明符seq应仅出现在虚拟成员功能(10.3)。

因此声明

void foo() final;

实际上与相同

virtual void foo() final override;

由于两者都要求foo重写某些东西——第二个声明使用override,第一个声明有效,当且仅当foo是隐式虚拟的,即当foo重写基类中名为foo的虚拟函数时,,这使派生函数中的foo自动成为虚拟函数因此,在出现final而不是virtual的声明中,override将是多余的
尽管如此,后一种声明表达的意图要清楚得多,绝对应该是可取的。

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

我可以想到创建一个虚拟且立即结束的函数的一个原因是,如果你想防止派生类给出相同的名称&参数具有不同的含义。

(如果你赶时间,跳到最后看结论。)

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()。我们知道CCD_ 38和CCD_。

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

  3. 比较f4()f5()。我们知道,如果一个成员函数是用virtual声明的,并且它不是继承层次结构中的第一个虚拟函数,那么我们应该使用override来指定覆盖关系。否则,我们可能会意外地在派生类中添加新的虚拟函数。

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

  5. 比较f7()f8()。我们知道,我们甚至可以覆盖基类函数,使其成为一个新的虚拟函数。(这意味着从D派生的类的任何成员函数f8()都将是虚拟的。)(在实践中也应该避免这种情况。)

  6. 比较f7()f9()。我们知道,当我们想重写派生类中的虚拟函数而忘记在基类中添加关键字virtual时,override可以帮助我们找到错误。

总之,在我看来,最佳实践是:

  • 只有在基类中第一个虚拟函数的声明中使用virtual
  • 始终使用override在派生类中指定重写虚拟函数,除非还指定了final

编译以下代码(带有final说明符)。但当final替换为override final时,编译失败。因此,CCD_ 72比仅仅CCD_ 73传递更多的信息(并防止编译)。

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

本质上,override final说这个方法不能在任何派生类中重写,并且这个方法重写基类中的虚拟方法。单独的final并没有指定基类覆盖部分。

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

override关键字很重要,因为它强制您实际上正在重写一个虚拟函数(而不是声明一个新的无关函数)。请参阅这篇关于override 的文章

长话短说,他们每个人都有自己的特殊目的,两者兼而有之通常是正确的。