最终是否意味着覆盖?

时间:2015-04-02 11:58:45

标签: c++ override final virtual-functions

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

我对final关键字的理解是它告诉编译器没有类会覆盖这个virtual函数。

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

5 个答案:

答案 0 :(得分:38)

final不需要该函数首先覆盖任何内容。其效果在[class.virtual] / 4中定义为

  

如果某个班级f中的虚拟函数B标有。{    virt-specifier final以及来自D函数B的类D::f   覆盖B::f,该程序格式不正确。

那就是它。现在override final只是意味着 “此函数会覆盖基类1(override),并且不能自行覆盖(final)。”

final自己会强加一个较弱的要求。 overridefinal有独立行为。


请注意,final只能用于虚拟功能 - [class.mem] / 8

  

virt-specifier-seq 只出现在a的声明中   虚拟成员函数(10.3)。

因此声明

void foo() final;

实际上与

相同
virtual void foo() final override;

由于两者都要求foo覆盖某些内容 - 第二个声明使用override,第一个声明是有效的,当且仅当foo隐式虚拟时,即foo覆盖基类中名为foo的虚函数时,会使派生的foo自动虚拟。因此override将是多余的发生final而非virtual的声明 尽管如此,后一种声明表达的意图更加清晰,绝对应该是首选。

答案 1 :(得分:16)

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

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

答案 2 :(得分:4)

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

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

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

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

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

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

  7. 总之,我自己观点中的最佳做法是:

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

答案 3 :(得分:1)

final并不一定意味着override。实际上,您可以声明virtual函数,并立即声明final see herefinal关键字只是声明没有派生的class可以创建此函数的覆盖。

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

长话短说,它们各自都有自己的特定目的,使用它们通常都是正确的。

答案 4 :(得分:1)

以下代码(使用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没有指定基类覆盖部分。