'override'关键字只是对被覆盖的虚拟方法的检查吗?

Is the 'override' keyword just a check for a overridden virtual method?

本文关键字:方法 虚拟 检查 关键字 override 覆盖      更新时间:2023-10-16

据我所知,在C++11中引入override关键字只不过是一种检查,以确保所实现的函数是基类中virtual函数的overrideing。

就是这样吗?

这确实是个想法。关键是你要明确你的意思,这样就可以诊断出一个无声的错误:

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"意味着该方法旨在重写基类上的(虚拟)方法。重写方法必须具有与其打算重写的方法相同的签名(至少对于输入参数)。

为什么这是必要的?好吧,以下两种常见的错误情况是可以防止的:

  1. 在新方法中有一个类型打字错误。编译器没有意识到它打算编写以前的方法,只是将它作为一个新方法添加到类中。问题是旧方法仍然存在,新方法只是作为过载添加的。在这种情况下,所有对旧方法的调用都将像以前一样工作,而不会改变行为(这正是重写的目的)。

  2. 人们忘记将超类中的方法声明为"虚拟",但仍然试图在子类中重写它。虽然这显然会被接受,但行为不会完全符合预期:该方法不是虚拟的,因此通过指向超类的指针进行访问将最终调用旧的(superclass')方法,而不是新的(subclass')方法。

添加"override"可以清楚地消除歧义:通过它,可以告诉编译器需要三件事:

  1. 在超类中有一个同名的方法
  2. 超类中的这个方法被声明为"virtual"(也就是说,打算重写)
  3. 超类中的方法与子类(重写方法)中的方法具有相同的(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,但在需要的情况下,派生类中没有进一步的重写。