【问题标题】:Is the 'override' keyword just a check for a overridden virtual method?'override' 关键字只是检查覆盖的虚拟方法吗?
【发布时间】:2023-03-05 01:20:01
【问题描述】:

据我了解,在 C++11 中引入 override 关键字无非是检查以确保正在实现的函数是基中 virtual 函数的 overrideing类。

是吗?

【问题讨论】:

  • 是的。⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣
  • 这不是一个双重检查。这是唯一的检查。
  • 嘿,override 不是关键字,它是一种语法糖。诠释覆盖=42; // 好的
  • 它还提高了可读性,解释了声明的函数被覆盖;)
  • 那么,呃... C++11 什么时候会变得足够标准,以至于他们开始在我当地的 4 年教这样的东西?他们什么时候知道?!

标签: c++ c++11 overriding virtual-functions c++-faq


【解决方案1】:

确实是这个想法。关键是您要明确说明您的意思,以便可以诊断出其他无声的错误:

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

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

上面的代码可以编译,但不是您的意思(注意缺少的const)。如果你改为使用virtual int foo() override,那么你会得到一个编译器错误,表明你的函数实际上没有覆盖任何东西。

【讨论】:

  • +1:不幸的是,当人们建议新的override 功能“修复”这个问题时,这有点牵强。你必须记得使用它,就像你应该记得写const ;)
  • @aschepler explicit 类定义会做什么?从来没有听说过。
  • @LightnessRacesinOrbit:是的,这不是万无一失的;然而,记住一个一般规则(当一个人打算这样做时疯狂地写override)比记住极端情况更有可能,即复制不同原型的函数没有通用性,只有不规则性,比如缺少const或写char而不是int等。
  • @Light,override 说明符的最佳用例在this answer 中提到,这比直接更具未来感。答案表明,将overridevirtual 方法一起使用。将来当一个人错误地更改签名时,它的用处就会发挥作用。
  • 核心准则 C.128 规定“虚拟函数应准确指定 virtual、override 或 final 之一。” github.com/isocpp/CppCoreGuidelines/blob/master/…
【解决方案2】:

维基百科引用:

覆盖特殊标识符意味着编译器将检查基类以查看是否存在具有此确切签名的虚函数。如果没有,编译器会报错。

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

编辑(试图改进一点答案):

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

为什么这是必要的?那么,下面两种常见的错误情况就被阻止了:

  1. 在新方法中输入错误的类型。编译器不知道它打算编写以前的方法,只是将其作为新方法添加到类中。问题是旧方法仍然存在,新方法只是作为重载添加。在这种情况下,所有对旧方法的调用都将像以前一样运行,而不会改变行为(这本来就是重写的目的)。

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

添加“覆盖”清楚地消除了这方面的歧义:通过这一点,我们告诉编译器期待三件事:

  1. 超类中有同名方法
  2. 超类中的此方法被声明为“虚拟”(也就是说,打算重写)
  3. 超类中的方法与子类中的方法(重写方法)具有相同的(输入*)签名

如果其中任何一个为假,则会发出错误信号。

* 注意:输出参数有时是不同的,但相关的类型。如果有兴趣,请阅读协变和逆变转换。

【讨论】:

    【解决方案3】:

    发现“override”在有人更新基类虚拟方法签名(例如添加可选参数)但忘记更新派生类方法签名时很有用。在这种情况下,基类和派生类之间的方法不再是多态关系。没有覆盖声明,很难发现这种错误。

    【讨论】:

    • +1。虽然override 是发现此类问题的好方法,但良好的单元测试覆盖率也应该有所帮助。
    • 这正是我对这个新说明符如此兴奋的原因。唯一的问题是必须已经应用此功能,以防止因基类更改而导致的错误。 ;-)
    【解决方案4】:

    是的,就是这样。这是一项检查,以确保不会尝试覆盖并通过拙劣的签名将其搞砸。这是一个 Wiki 页面,详细解释了这一点,并有一个简短的说明性示例:

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

    【讨论】:

      【解决方案5】:

      C++17 标准草案

      在检查了C++17 N4659 standard draft 上的所有override 命中后,我能找到override 标识符的唯一引用是:

      5 如果一个虚函数被 virt-specifier 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
      }
      

      —结束示例]

      所以我认为可能炸毁错误的程序实际上是唯一的效果。

      【讨论】:

        【解决方案6】:

        澄清有关虚拟的一切(因为我一直在反复遇到这个问题!)。

        • virtual基类 告诉派生类一个函数可以被覆盖
          • 无需在派生类中使用virtual。如果一个函数具有相同的名称/参数类型list/cv-qual/ref-qual,它将自动正确使用。
          • (实际上,在派生类中使用virtual 会产生细微的错误,见下文)
        • override派生类可选说明符,用于捕获错误和文档代码:
          • 告诉编译器:“确保有一个我正在重写的 EXACT 虚函数”
            • 避免错误地创建会导致细微错误的不同函数签名(即 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,但如果您希望在派生类中不再进行覆盖

        【讨论】:

          猜你喜欢
          • 2015-05-22
          • 2012-10-26
          • 2016-01-24
          • 1970-01-01
          • 2011-01-28
          • 2012-06-19
          • 1970-01-01
          • 2018-03-06
          相关资源
          最近更新 更多