“override”关键字只是用来检查是否重写虚方法吗?

274
据我了解,C++11中引入的override关键字仅用于检查实现的函数是否是覆盖基类中的virtual函数。
就这样吗?

62
是的。 - R. Martinho Fernandes
15
虽然不是双重检查,但这是唯一的检查。 - Nikos C.
18
嘿,override 不是一个关键词,它是一种语法糖。 int override=42; //可以 - KAlO2
7
那么,嗯... C++11 何时成为足够标准,以至于他们开始在我当地的四年制学校教授这样的内容?他们什么时候会知道呢? - CinchBlue
3
当然,override作为普通标识符无法与override限定符发生冲突,因为后者只能出现在一个位置,而前者在语法上永远无效。这正是他们设置这种方式的原因,以便在单个上下文中使overridefinal具有特殊含义,并在不剥夺用户以前可用的将它们用作普通标识符的能力的同时。 - underscore_d
显示剩余3条评论
6个回答

304

这确实是个好主意。关键在于你要明确表达自己的意思,这样就可以诊断出本来无声的错误:

struct Base
{
    virtual int foo() const;
};

struct Derived : Base
{
    virtual int foo()   // whoops!
    {
       // ...
    }
};

上述代码可以编译通过,但可能不是你本意(注意缺少const)。如果你改为写成virtual int foo() override,那么编译器会报错,因为你定义的函数实际上没有覆盖任何内容。


81
尽管很遗憾,当人们建议新的"override"特性可以“修复”这个问题时,这有点误导人。你需要记住要使用它,就像你应该记住写"const"一样。 - Lightness Races in Orbit
1
@aschepler,explicit类定义会做什么?我从未听说过。 - Christian Rau
21
@LightnessRacesinOrbit: 是的,它不是绝对可靠的;然而,记住一个通用规则(疯狂地写override时意图就是这样做)比记住边角案例更可能,即复制不同原型函数中的函数没有通用性,只有像缺少const或将char写成int之类的异常情况。 - legends2k
2
@Light,override关键字最好的使用案例在这个答案中提到,它比较具有未来性而不是立即性。该答案建议将overridevirtual方法一起使用。在未来,当一个人错误地更改了签名时,它的用处就会显现出来。 - iammilind
2
核心指南 C.128 规定:“虚函数应该精确地指定 virtual、override 或 final 中的一个。” https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rh-override - parsley72
显示剩余8条评论

41

维基百科引用:

"override"特殊标识符意味着编译器将检查基类,以查看是否存在具有此完全签名的虚函数。如果没有,则编译器将报错。

http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final

编辑(试图改善一下答案):

将一个方法声明为"override"意味着该方法旨在重写基类上的一个(虚)方法。重写方法必须具有与它打算重写的方法相同的签名(至少对于输入参数)。

这为什么是必要的呢?嗯,以下两种常见的错误情况被防止了:

  1. 新方法中拼错类型。编译器不知道它打算重写之前的方法,只是将其作为新方法添加到类中。问题是旧方法仍然存在,新方法只是作为重载添加。在这种情况下,所有调用旧方法的方式将像以前一样工作,行为不会发生任何变化(这本来是重写的目的)。

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

添加"override"明确消除了歧义: 通过这个,你告诉编译器有三件事正在等待:

  1. 超类中存在一个同名的方法
  2. 该超类方法被声明为"virtual"(也就是,打算被重写)
  3. 超类中的方法与子类中的方法(重写方法)具有相同的(输入)签名
如果其中任何一项为假,那么就会发出错误信号。
* 注意: 输出参数有时是不同但相关的类型。如果感兴趣,请阅读有关协变和逆变转换的内容。

39
发现当某人更新了基类的虚拟方法签名,比如添加了一个可选参数但忘记更新派生类方法签名时,“override”非常有用。在这种情况下,基类和派生类之间的方法不再具有多态关系。如果没有“override”声明,则很难发现此类错误。

1
尽管 override 是发现此类问题的好方法,但良好的单元测试覆盖率也应该有所帮助。+1 - Disillusioned
1
这正是我对那个新的限定符感到如此兴奋的原因。唯一的问题是,必须已经应用了此功能,以防止基类更改导致的错误。;-) - Wolf

6

4

C++17标准草案

在查看了C++17 N4659标准草案中所有关于override的引用后,我只能找到一个与override标识符有关的参考:

5 If a virtual function is marked with the virt-specifier override and does not override a member function of a base class, the program is ill-formed. [ Example:

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
}

— end example ]

我认为可能唯一的影响是错误程序被炸掉。


3

为了澄清所有有关虚拟的事情(因为我一直遇到这个问题!)。

  • virtual是用于基类告诉派生类可以覆盖一个函数
    • 不需要在派生类中使用virtual。如果具有相同的名称/参数类型列表/cv-qual/ref-qual,则会自动正确使用该函数。
    • (实际上,在派生类中使用virtual可能会创建微妙的错误,请参见下文)
  • override是一个可选说明符,用于派生类来捕获错误并记录代码:
    • 告诉编译器:“确保有一个完全重写的虚函数”
      • 避免意外创建不同的函数签名而导致微妙的错误(即两个略有不同的函数本应相同)
      • 告诉程序员这是覆盖虚函数

所以给定:

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,原因相同,但是当你想要在派生类中不再进行进一步的重写时使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接