C++中的“override”关键字是用来做什么的?

313

我是C++的初学者。在我正在使用的头文件中,我遇到了override关键字。请问,override的实际用途是什么,最好能举个例子易于理解。


1
请注意,C++11引入了final关键字。 - Jesse Good
18
@JesseGood 再次强调:final 也不是一个关键字!!!请注意。 - zaufi
11
@zaufi 这是一个上下文关键字 - phuclv
这是受 Java 的 @Override 启发的东西,让编译器为您进行编译时检查。 - JP Zhang
4个回答

487
override关键字有两个作用:
  1. 它向代码读者显示“这是一个重写基类虚方法的派生类虚方法”。
  2. 编译器也知道这是一个重写,因此它可以“检查”您是否更改/添加了您认为是重写的新方法。

解释后一点:

class base
{
  public:
    virtual int foo(float x) = 0; 
};


class derived: public base
{
   public:
     int foo(float x) override { ... } // OK
};

class derived2: public base
{
   public:
     int foo(int x) override { ... } // ERROR
};

derived2 中,编译器将会因为“改变类型”而发出错误。如果没有使用 override,最多编译器只会警告“你正在通过相同的名称隐藏虚方法”。


1
函数 foo 的头文件在类 derived2 中不应该与之前的类 base 中的相似吗?我使用 VS2017 编译时出现了编译错误。我的意思是在 derived2 中,foo 的头文件必须是:int foo(float x) override {...} - Fatemeh Karimi
71
这恰恰是重点,代码示例展示了如何使用override来检测错误! - Mats Petersson
我认为第三点是覆盖可以用来检测将要被覆盖的函数(属于父类/接口)是否被删除(而不是替代)。如果子类期望回调但由于库更改等原因永远不会发生,这非常有用。 - Hei
我认为编译器无法在不扫描整个调用图的情况下完成这项任务,如果编译器这样做了,它就不需要 override 来理解。这真的很难实现。 - Mats Petersson

87

作为所有答案的补充,顺便说一下: override不是关键字,而是一种特殊的标识符!它只有在声明/定义虚函数的上下文中才有意义,在其他情况下它只是一个普通的标识符。详情请参阅标准的2.11.2节。

#include <iostream>

struct base
{
    virtual void foo() = 0;
};

struct derived : base
{
    virtual void foo() override
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

int main()
{
    base* override = new derived();
    override->foo();
    return 0;
}

输出:

zaufi@gentop /work/tests $ g++ -std=c++11 -o override-test override-test.cc
zaufi@gentop /work/tests $ ./override-test
virtual void derived::foo()

3
它能够正常工作(在这里和final中)是因为你不能在这些上下文关键字所在的位置使用普通标识符。 - CTMacUser
1
为什么它不被禁止了? - Ferenc Dajka
7
  1. 为什么应该这样做?
  2. 突然添加一个新的关键词(即禁止在其他地方使用它)会破坏向后兼容性。
- idmean
1
序数还是普通的? - v.oddou
什么是“标准”?谷歌有无数个被称为“标准”的东西...请给个链接? - krubo
1
@krubo https://isocpp.org/std/the-standard 词语“标准”指的是国际上正式达成的协议。例如,米的长度定义是一个标准。 - Michael

13

override 是 C++11 的一个关键字,意思是一个方法是从父类继承过来的 "override" 方法。考虑以下例子:

class Foo
{
public:
    virtual void func1();
};

class Bar : public Foo
{
public:
    void func1() override;
};

如果 B::func1() 的签名和 A::func1() 的签名不相等,编译时会生成一个错误,因为 B::func1() 没有覆盖 A::func1(),而是定义了一个名为 func1() 的新方法。

15
根据C++规范,“override”是一个“具有特殊含义的标识符”。但在C#中,这样的东西被称为上下文关键字。 - Denis Gladkiy
我认为这段代码有些不正确。为了让编译器发出错误,派生类Bar中的签名必须不同,例如void func1(int v) override;,以便编译器发出像marked 'override', but does not override这样的错误。换句话说,奇怪的是,override似乎是编译器在派生类中使用的提示,以验证基类中存在相同的签名。请参见上面Mats Petersson的答案。 - luv2learn

0

维基百科说:

方法重写(Method overriding)是面向对象编程中的一种语言特性,它允许子类或子类提供一个已经由其超类或父类提供的方法的特定实现。

具体来说,当您有一个具有void hello()函数的对象foo时:

class foo {
    virtual void hello(); // Code : printf("Hello!");
};

foo的子类也会有一个hello()函数:

class bar : foo {
    // no functions in here but yet, you can call
    // bar.hello()
};

然而,当从一个bar对象调用hello()函数时,您可能希望打印出"Hello Bar!"。您可以使用override来实现这一点。

class bar : foo {
    virtual void hello() override; // Code : printf("Hello Bar!");
};

25
在C++中,你不需要使用override标识符来完成这个操作,它只是强制你正确地执行它。 - Goose
1
您好,实际上维基百科的概念作为一般基础是很好的,但正如Goose所提到的那样,C++的具体细节增加了一些复杂性:它用在派生类中以帮助编译器识别代码中的问题,在这种情况下,虚函数存在于基类中。细节决定成败。请参见上述Mark Petersson的评论。 - luv2learn

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