Final 是否意味着覆盖?

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

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

那么 override final是多余的吗?看起来编译得很好.哪些信息是 override final传达的,而 final没有?这种组合的用例是什么?

28330 次浏览

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

我能想到的创建一个虚拟的、立即的最终函数的一个原因是,如果你想给 预防一个派生类赋予相同的名称和参数一个不同的含义。

没有 final并不一定意味着 override。实际上,您可以声明一个立即声明 final 看这里virtual函数。final关键字只是说明没有派生的 class可以创建此函数的重写。

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

所以长话短说,它们各自服务于自己的特定目的,通常正确的做法是同时使用两者。

final首先不要求函数覆盖任何内容

如果某个类 B中的虚函数 fVirt 指定符 final,在从 B派生的类 D中,函数 D::f 重写 B::f时,程序格式不正确。

就是这样,现在 override final意味着
“此函数重写基类1(override) ,不能重写本身(final)。”
单独的 final会强加一个较弱的要求。 overridefinal有独立的行为。


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

Virt-specfier-seq只应出现在 虚拟成员函数(10.3)。

所以我才发表声明

void foo() final;

实际上等同于

virtual void foo() final override;

由于两者都要求 foo覆盖某些内容——第二个声明使用 override,第一个声明使用当且仅当 foo是隐式虚拟的时候才有效,也就是说 foo在基类中重写名为 foo的虚函数时,将使派生类中的 foo自动成为虚函数。。因此,在发生 final而不是 virtual的声明中,override是多余的。
尽管如此,后一项声明表达的意图要清楚得多,而且肯定应该更为可取。

编译以下代码(使用 final说明符)。但是当 finaloverride 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说这个方法不能在任何派生类 还有中重写,这个方法重写基类中的虚方法。单独的 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(),我们知道 overridefinal在语义上是独立的。

    • 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