override
关键字表示一个给定的声明实现了一个基类 virtual
方法,并且如果没有找到匹配的基方法,则应该编译失败。我对
final
关键字的理解是它告诉编译器不允许任何类覆盖此 virtual
函数。那么,
override final
是多余的吗? 似乎可以编译通过 override final
传达了哪些信息,而 final
却没有呢?这种组合的使用情况是什么?override
关键字表示一个给定的声明实现了一个基类 virtual
方法,并且如果没有找到匹配的基方法,则应该编译失败。final
关键字的理解是它告诉编译器不允许任何类覆盖此 virtual
函数。override final
是多余的吗? 似乎可以编译通过 override final
传达了哪些信息,而 final
却没有呢?这种组合的使用情况是什么?final
本身并不要求函数首先覆盖任何东西。它在[class.virtual] / 4中定义为:
如果某个类的虚拟函数
f
被标记为 virt-specifierfinal ,而在从派生的类中,函数重载了,则该程序是非法的。
就这样。现在override final 仅意味着
“此函数覆盖了一个基类函数( override ),并且自身不能被覆盖( final )。”
final 本身会施加较弱的要求。
override 和 final 具有独立的行为。
注意, final 只能用于虚拟函数 - [class.mem] / 8
virt-specifier-seq 只能出现在
虚拟成员函数的声明(10.3)。
因此声明如下:
void foo() final;
实际上相当于
virtual void foo() final override;
由于两者都需要使用foo
来覆盖某些东西 - 第二个声明通过使用override
,第一个声明是当且仅当foo
隐式地成为虚拟函数时有效,也就是说当foo
在基类中重写一个名为foo
的虚拟函数时,这使得派生类中的foo
自动变成虚拟函数。 因此,在只有final
但不是virtual
的情况下,override
是多余的。
不过,后面的声明更清楚地表达了意图,应该优先选择。
final
并不一定意味着该函数被覆盖。如果在继承层次结构的第一个声明中将虚函数声明为final
,这是完全有效的(尽管有些可疑)。
我能想到创建虚函数并立即使用final
的一个原因是,如果您想要防止派生类为相同名称和参数赋予不同的含义。
dynamic_cast
必须为其工作,例如。我相信虚函数的 vtable 实现也需要 vtable 才能使 dynamic_cast
工作。 - Angew is no longer proud of SODerived2
无法隐藏mustRemainBase
。 - Angew is no longer proud of SO#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()
。我们知道,甚至可以覆盖一个基类函数并将其变为一个新的虚函数。(这意味着从 D
派生的任何成员函数 f8()
都将是虚函数。)(在实践中也应该避免这样做。)
比较 f7()
和 f9()
。我们知道,override
可以在要覆盖派生类中的虚函数时帮助我们找到错误,而忘记在基类中添加关键字 virtual
。
总之,我认为最佳实践是:
virtual
;final
,否则始终使用 override
来指定覆盖虚函数。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
单独使用时不指定基类的覆盖部分。
void foo() final
可以实现这一点。 - Johan Lundberg“override”关键字非常重要,因为它强制执行实际上是覆盖虚拟函数(而不是声明新的无关函数)。请参见有关“override”的此篇文章
长话短说,它们各自具有特定的目的,通常同时使用是正确的。
virtual void f() final override
和void f() final
在某种意义上是等价的,因为两者如果没有覆盖任何东西都会失败。final
仅适用于虚函数,而后一种声明的f
只有当它覆盖一个函数时才是虚函数。尽管如此,后者的错误消息可能不太精确。 - Markus Mayrvirtual
相同:它没有增加任何价值。但是这种写法当然比写成virtual void foo() final;
好。然而,后面的声明表达意图更清晰。 - Wolffinal
必须是virtual
(我正在学习这些新功能),并且由于您省略了virtual
关键字,如果它不是override
,则编译将无法通过。或者也许我忽略了一种可能导致不良行为的情况?在下面Angew的答案中,我只是试图弄清楚非覆盖终极虚拟的用法... - Wolf