【问题标题】:What is the 'override' keyword in C++ used for? [duplicate]C++中的'override'关键字是干什么用的? [复制]
【发布时间】:2013-08-14 10:23:29
【问题描述】:

我是 C++ 的初学者。我遇到了我正在处理的头文件中使用的override 关键字。请问override的真正用途是什么,或许举个例子就很容易理解了。

【问题讨论】:

  • 注意c++11引入了final关键字。
  • @JesseGood 再次:final 也不是关键字 !!!
  • @zaufi 这是contextual keyword
  • 受 Java 的 @Override 启发,让编译器为你做编译时检查

标签: c++ c++11 overriding


【解决方案1】:

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,编译器最多会发出警告“你正在隐藏同名的虚拟方法”。

【讨论】:

  • 在类 derived2 中* foo* 的函数头不应该相似吗?我用 VS2017 编译它并得到一个编译错误。我的意思是 inderived2 foo's 标头必须是: *int foo (float x) override {...} *
  • 嗯,这就是重点,代码示例展示了如何使用 override 来检测错误!
  • 我认为#3是override可以用来检测要被覆盖的函数(属于父类/接口)被移除(不替代)。如果子类期望回调但由于库更改等原因它永远不会发生,这很有用。
  • 我不认为编译器可以在不扫描所有调用图的情况下做到这一点,如果编译器这样做,它就不需要override 来理解这一点。要做到这一点真的很难。
【解决方案2】:

作为所有答案的附录,仅供参考: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()

【讨论】:

  • 它可以工作(这里和final),因为您不能使用常规标识符来放置这些上下文关键字。
  • 为什么它不被禁止?
  • @FerencDajka 1. 为什么会这样? 2. 突然添加一个新关键字(即禁止在其他任何地方使用它)会破坏向后兼容性。
  • 序数还是普通?
  • @krubo isocpp.org/std/the-standard “标准”一词指的是国际上正式同意的东西。比如一米多长的定义就是一个标准
【解决方案3】:

override 是 C++11 关键字,这意味着方法是基类方法的“覆盖”。考虑这个例子:

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

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

如果B::func1() 签名不等于A::func1() 签名,则会生成编译错误,因为B::func1() 没有覆盖A::func1(),它将定义一个名为func1() 的新方法。

【讨论】:

  • 根据 C++ 规范,“覆盖”是一个“具有特殊含义的标识符”。但是在 C# 中这样的东西被称为上下文关键字。
  • 我认为这个 sn-p 有一些不正确的地方。为了让编译器发出错误,派生类 Bar 中的签名必须不同,如 void func1(int v) override; 中的签名,以便编译器发出类似“标记为“覆盖”但不覆盖的错误。 IOW:奇怪的是,覆盖似乎是编译器在派生类中使用的提示,以验证基类中是否存在相同的签名。请参阅上面的Mats Petersson 答案。
【解决方案4】:

维基百科说:

在面向对象的编程中,方法覆盖是一种语言功能,它允许子类或子类提供已由其超类或父类之一提供的方法的特定实现。

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

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

foo 的子节点,也会有一个 hello() 函数:

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

但是,您可能想要打印“Hello Bar!”当从 bar 对象调用 hello() 函数时。您可以使用覆盖来做到这一点

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

【讨论】:

  • 在 C++ 中您不需要覆盖标识符来执行此操作,它只是强制您正确执行此操作。
  • 嗨,维基百科的概念确实可以作为一般基础,但正如 Goose 所提到的,C++ 的细节增加了一个转折:它在派生类中用于帮助编译器识别代码中的问题,在这种情况下,虚函数存在于基础中。细节决定成败。请参阅上面的Marks Petersson 评论。
猜你喜欢
  • 2013-07-31
  • 1970-01-01
  • 2010-10-08
  • 2019-04-23
  • 2011-09-28
  • 2011-03-17
  • 2014-10-04
  • 2011-08-17
  • 2012-03-15
相关资源
最近更新 更多