'override'关键字只是对被覆盖的虚拟方法的检查吗?
Is the 'override' keyword just a check for a overridden virtual method?
据我所知,在C++11中引入override
关键字只不过是一种检查,以确保所实现的函数是基类中virtual
函数的override
ing。
就是这样吗?
这确实是个想法。关键是你要明确你的意思,这样就可以诊断出一个无声的错误:
struct Base
{
virtual int foo() const;
};
struct Derived : Base
{
virtual int foo() // whoops!
{
// ...
}
};
上面的代码可以编译,但不是您想要的(注意缺少const
)。如果您改为virtual int foo() override
,那么您将得到一个编译器错误,即您的函数实际上并没有覆盖任何内容。
维基百科引文:
重写特殊标识符意味着编译器将检查基类,以查看是否存在具有此确切签名的虚拟函数。如果没有,编译器就会出错。
http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final
编辑(试图改进一点答案):
将方法声明为"override"意味着该方法旨在重写基类上的(虚拟)方法。重写方法必须具有与其打算重写的方法相同的签名(至少对于输入参数)。
为什么这是必要的?好吧,以下两种常见的错误情况是可以防止的:
-
在新方法中有一个类型打字错误。编译器没有意识到它打算编写以前的方法,只是将它作为一个新方法添加到类中。问题是旧方法仍然存在,新方法只是作为过载添加的。在这种情况下,所有对旧方法的调用都将像以前一样工作,而不会改变行为(这正是重写的目的)。
-
人们忘记将超类中的方法声明为"虚拟",但仍然试图在子类中重写它。虽然这显然会被接受,但行为不会完全符合预期:该方法不是虚拟的,因此通过指向超类的指针进行访问将最终调用旧的(superclass')方法,而不是新的(subclass')方法。
添加"override"可以清楚地消除歧义:通过它,可以告诉编译器需要三件事:
- 在超类中有一个同名的方法
- 超类中的这个方法被声明为"virtual"(也就是说,打算重写)
- 超类中的方法与子类(重写方法)中的方法具有相同的(input*)签名
如果其中任何一个为false,则会发出错误信号。
*注意:输出参数有时是不同但相关的类型。如果感兴趣,请阅读有关协变和逆变换的内容
当有人更新基类虚拟方法签名(如添加可选参数)但忘记更新派生类方法签名时,发现">override"非常有用。在这种情况下,基类和派生类之间的方法不再是多态关系。如果没有override声明,就很难发现这种bug。
是的,确实如此。这是一项检查,以确保不会尝试重写并通过拙劣的签名将其搞砸。这里有一个Wiki页面详细解释了这一点,并有一个简短的示例:
http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final
C++17标准草案
在浏览了C++17 N4659标准草案上的所有override
点击后,我能找到的唯一关于override
标识符的参考是:
5如果虚拟函数标记有virt说明符override,并且不重写基类,程序格式不正确。[示例:
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
。如果一个函数具有相同的名称/参数类型列表/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
,但在需要的情况下,派生类中没有进一步的重写。
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?
- 如何编写 operator= 用于使用虚拟方法与非平凡成员的匿名联合
- 让编译器告诉什么确切的纯虚拟方法使结构抽象?
- 使用模板而不是虚拟方法的管道模式
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 从纯虚拟类 (A) 派生的指针无法访问来自纯类 (B) 的重载方法
- 为什么调用没有正文的纯虚拟方法不会导致链接器错误?
- 出于什么目的,非虚拟方法将与C++一起使用?
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 如何重写继承的嵌套类中存在的虚拟方法
- 私有虚拟方法有什么用?
- 派生类中纯虚拟基方法的专业化
- 基类可以声明虚拟方法但不定义它吗?仍然在派生类中定义
- googletest:测试基类具有纯虚拟方法的派生类时的核心转储
- 确保模拟的 GTest 方法覆盖虚拟方法
- 使用回调函数从构造函数调用虚拟/派生方法的替代方法?
- 如何调用孩子的方法:虚拟关键字不起作用