提问人:aiao 提问时间:12/14/2012 最后编辑:sbiaiao 更新时间:11/2/2021 访问量:56822
“override”关键字只是对被覆盖的虚拟方法的检查吗?
Is the 'override' keyword just a check for a overridden virtual method?
问:
据我了解,在 C++11 中引入关键字只不过是检查以确保正在实现的函数是基类中函数的 ing。override
override
virtual
是这样吗?
答:
是的,就是这样。这是一项检查,以确保人们不会尝试覆盖并通过拙劣的签名将其搞砸。这里有一个 Wiki 页面,详细解释了这一点,并有一个简短的说明性示例:
http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final
维基百科引用:
重写特殊标识符意味着编译器将检查基类,以查看是否存在具有此确切签名的虚函数。如果没有,编译器将出错。
http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final
编辑(试图改进一点答案):
将方法声明为“重写”意味着该方法旨在重写基类上的(虚拟)方法。重写方法必须与它打算重写的方法具有相同的签名(至少对于输入参数而言)。
为什么有必要这样做?好吧,可以防止以下两种常见的错误情况:
一个在新方法中错误地键入了类型。编译器不知道它打算编写以前的方法,只是将其作为新方法添加到类中。问题是旧方法仍然存在,新方法只是作为重载添加的。在这种情况下,对旧方法的所有调用都将像以前一样运行,行为没有任何变化(这将是重写的真正目的)。
忘记将超类中的方法声明为“虚拟”,但仍然尝试在子类中重写它。虽然这显然会被接受,但行为不会完全符合预期:该方法不是虚拟的,因此通过指向超类的指针进行访问将结束调用旧的(超类)方法而不是新的(子类)方法。
添加“override”显然可以消除歧义:通过这一点,人们可以告诉编译器有三件事是预期的:
- 超类中有一个同名的方法
- 超类中的此方法被声明为“虚拟”(这意味着,要重写)
- 超类中的方法与子类中的方法(重写方法)具有相同的 (input*) 签名
如果其中任何一个为 false,则发出错误信号。
* 注意:输出参数有时具有不同但相关的类型。如果有兴趣,请阅读协变变换和逆变变换。
这确实是这个想法。关键是你要明确你的意思,这样就可以诊断出一个原本无声的错误:
struct Base
{
virtual int foo() const;
};
struct Derived : Base
{
virtual int foo() // whoops!
{
// ...
}
};
上面的代码可以编译,但不是你的意思(注意缺少)。如果你说,那么你会得到一个编译器错误,你的函数实际上没有覆盖任何东西。const
virtual int foo() override
评论
override
const
;)
explicit
override
const
char
int
override
override
virtual
当有人更新基类虚拟方法签名(例如添加可选参数但忘记更新派生类方法签名)时,发现“覆盖”很有用。在这种情况下,基类和派生类之间的方法不再是多态关系。如果没有覆盖声明,就很难发现这种错误。
评论
override
C++17 标准草案
在查看了 C++17 N4659 标准草案的所有命中后,我能找到的唯一标识符参考是:override
override
5 如果一个虚函数被标记为 virt-specifier 覆盖,并且没有覆盖 基类时,程序格式不正确。[ 示例:
struct B { virtual void f(int); }; struct D : B { virtual void f(long) override; // error: wrong signature overriding B::f virtual void f(int) override; // OK }
— 结束示例 ]
所以我认为可能炸毁错误的程序实际上是唯一的效果。
澄清关于虚拟的一切(因为我反复遇到这个问题!
virtual
用于基类告诉派生类可以重写函数- 无需在派生类中使用
virtual
。如果函数具有相同的名称/参数类型 list/cv-qual/ref-qual,则会自动正确使用。 - (实际上,在派生类中使用可能会产生微妙的错误,见下文)
virtual
- 无需在派生类中使用
override
是派生类的可选说明符,用于捕获错误和文档代码:- 告诉编译器:“确保有一个我正在重写的确切虚函数”
- 避免错误地创建不同的函数签名,从而导致细微的错误(即 2 个本应相同的略有不同的函数)
- 告诉编码人员这是在覆盖虚拟函数
- 告诉编译器:“确保有一个我正在重写的确切虚函数”
所以给定:
class base
{
public:
virtual int foo(float x);
};
以下是一些不同的覆盖方式:
// AUTOMATIC virtual function (matches original, no keywords specified)
int foo(float x) { ; }
// Re-specifying "virtual" uselessly (+ see pitfalls below)
virtual int foo(float x) { ; }
// Potential issues: it is unknown if the author intended this to be a
// virtual function or not. Also, if the author DID intend a match but
// made a mistake (e.g. use "int" for the parameter), this will create
// a subtle bug where the wrong function is called with no warning anywhere:
int foo(int x) { ; } // SUBTLE, SILENT BUG! int instead of float param
virtual int foo(int x) { ; } // SUBTLE, SILENT BUG! int instead of float param
// Better approach: use the 'override' identifier to
// make sure the signature matches the original virtual function,
// and documents programmer intent.
int foo(float x) override { ; } // Compiler checks OK + tells coder this is virtual
int foo(int x) override { ; } // COMPILE ERROR, caught subtle bug
virtual int foo(int x) override { ; } // COMPILE ERROR, caught subtle bug
// (and redundant use of "virtual")
最后 (!),可以出于相同的原因使用说明符来代替,但如果您不希望在派生类中进一步重写。final
override
评论
override
override
override
final