重写纯虚函数时使用 `override` 有意义吗?

60
例如:
class Base {
  virtual void my_function() = 0;
};

class Derived : Base {
  void my_function() override;
};

据我所读,override 关键字用于确保我们在重写的函数中有正确的签名,并且它似乎只有这一个用途。

但是,在纯虚函数的情况下,如果在派生类(或者基类,具体取决于怎么理解)中使用了不正确的签名,编译器会抛出错误。那么,在 Derived::my_function() 声明末尾加上 override 是否有意义呢?


4
注意,抽象性是会传播的。如果在Derived类中提供了错误的签名,则Derived类也会变成抽象类。如果没有使用override关键字,可能无法在正确的位置检测到这一点。 - Some programmer dude
4
它确保如果基类与override断言不兼容更改,您的代码将无法编译。 - user207421
1
有目的地编写教育计划有助于推广优选编程实践。- T.J.Elgan - coderatchet
添加它的小好处是,一些IDE将使用它作为突出显示实现或跳转到父级的重点。 - Craig Taylor
4个回答

91

然而,在纯虚函数的情况下,如果我们在派生类中使用了不正确的签名,编译器会抛出错误

不,这段代码可以编译:


class Base {
  virtual void my_function() = 0;
};

class Derived : Base {
  void my_function(int);
//                 ^^^ mistake!
};

虽然这并不会:

class Base {
  virtual void my_function() = 0;
};

class Derived : Base {
  void my_function(int) override;
};

错误: void Derived::my_function(int) 标记为override, 但未覆盖


你提到的错误只会在实例化Derived时发生 - override 允许您更早地捕获错误并使Derived的定义更清晰/易读。


你可以在另一个类中添加运行时错误,并实现 Base::my_function,同时将其保留为纯虚函数。 - Yakk - Adam Nevraumont
4
等等,这里有一个重点吗?你接着谈论它的用处,但一开始只是说“没有”。 - Krupip
10
“不”是对编译器会抛出错误的说法的回应。 - Sebastian Redl
1
@templatetypedef 这就是重点,程序员在认为他们编写的签名应该完全出现在基类中时才会使用override。第一种情况是一个错误,因为程序员本意是要覆盖函数,但不小心写成了重载。 - Steve Cox
1
@templatetypedef 我宁愿不要等待编译系统逐个编译完我的整个项目并等到它尝试将所有内容链接在一起,最后才失败。 (或者在您尝试实例化Derived时在其他地方失败...)更好的方法是在编译该文件时就出现错误,并指向确切的那一行并说“这里的代码有误”。 - Andre Kostur
显示剩余7条评论

36

使用override关键字是一种良好的防御性实践,这是一个不错的想法。

Base的作者决定my_function不再是纯虚函数,同时还要添加一个新参数时,考虑重新设计。有了override,编译器会捕捉到这个问题;如果没有override,你的Derived类将继续编译。


11

是的!!

它提高了代码清晰度override 关键字可以防止歧义并传达其覆盖基类方法的含义。

防止可能的意外使用: 将来,如果基类更改方法签名(这里是 virtual),它会强制派生类相应更改(并产生编译器错误)。否则(没有 override 关键字),它可能被视为 method overload,这是不打算的。


1
这通常不会是方法重载,因为它被隐藏了。 - RiaD

3
通常不使用override会导致错误,只是把错误移到了别的地方。我认为最好在定义无法覆盖的方法时就能获得错误信息,而不是在实例化类时才获得。

但是,有一种方法可以防止运行时出现bug。

struct Base {
  virtual void foo(int x = 0) = 0;

  void foo(double d) {
      foo( (int)d );
  }
};
inline void Base::foo(int x) { std::cout << "Default foo(" << x << ")\n"; }

struct Derived:Base {
  using Base::foo;
  virtual void foo() { // oops, no int!
    std::cout << "Derived::foo()\n";
    Base::foo();
  }
};
struct Derived2:Derived {
  virtual void foo(int x=0) override {
    std::cout << "Derived2::foo()\n";
    Derived::foo(x);
  }
};

这里我们希望每个foo都调用它的父级foo。但是由于Derived::foo没有覆盖与Base::foo相同的签名,因此它不会被调用。
Derived中的foo()后面添加override,我们会得到一个编译时错误。
是的,我实现了纯虚函数Base::foo

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