15

当一个方法在类中声明virtual时,它在派生类中的覆盖也会被自动考虑virtual,并且 C++ 语言virtual在这种情况下使这个关键字是可选的:

class Base {
    virtual void f();
};
class Derived : public Base {
    void f(); // 'virtual' is optional but implied.
};

virtual我的问题是:选择可选的理由是什么?

我知道编译器并非绝对有必要被告知,但我认为如果编译器强制执行这样的约束,开发人员将受益。

例如,有时当我阅读其他人的代码时,我想知道一个方法是否是虚拟的,我必须追踪它的超类来确定这一点。并且一些编码标准(谷歌)使得将virtual关键字放在所有子类中是“必须的”。

4

5 回答 5

12

是的,在这种情况下让编译器强制执行虚拟会更好,我同意这是为了向后兼容而维护的设计错误。

但是,如果没有它,有一个技巧是不可能的:

class NonVirtualBase {
  void func() {};
};

class VirtualBase {
  virtual void func() = 0;
};

template<typename VirtualChoice>
class CompileTimeVirtualityChoice : public VirtualChoice {
  void func() {}
};

有了上面的内容,我们可以选择是否需要 func 的虚拟性:

CompileTimeVirtualityChoice<VirtualBase> -- func is virtual
CompileTimeVirtualityChoice<NonVirtualBase> -- func is not virtual

...但同意,对于寻求功能的虚拟性的成本来说,这是一个小好处,而我自己,我总是尝试在任何适用的地方输入 virtual 。

于 2010-06-03T07:28:24.913 回答
6

作为相关说明,在 C++0x 中,您可以选择通过新的属性语法强制显式地覆盖您的覆盖。

struct Base {
  virtual void Virtual();
  void NonVirtual();
};

struct Derived [[base_check]] : Base {
  //void Virtual(); //Error; didn't specify that you were overriding
  void Virtual [[override]](); //Not an error
  //void NonVirtual [[override]](); //Error; not virtual in Base
  //virtual void SomeRandomFunction [[override]](); //Error, doesn't exist in Base
};

[[hiding]]您还可以通过属性指定何时打算隐藏成员。它使您的代码更加冗长,但它可以在编译时捕获许多烦人的错误,就像您这样做void Vritual()而不是void Virtual()在您打算覆盖现有函数时最终引入了一个全新的函数。

于 2010-06-03T08:12:04.470 回答
3

设计的弱点,我同意。还认为,如果两种不同的事物有不同的语法,那就太好了:

  1. 声明一个虚函数。即派生类中可能被覆盖的函数。(这个东西实际上在 vtable 中增加了一个新的函数入口。)
  2. 覆盖派生类中的虚函数。

在覆盖函数时使用当前的 C++ 规则 - 很容易搞砸。如果您输入错误的函数名称(或在其参数列表中输入错误) - 那么您实际上执行的是 (1) 而不是 (2)。

而且您没有错误/警告。只需在运行时获得惊喜。

于 2010-06-03T07:33:40.173 回答
2

由于该语言不能强制执行“好的”风格,C++ 通常甚至不会尝试。至少在 IMO 中,在任何情况下,包括这样的冗余说明符是否都是好的样式,这是值得商榷的(就我个人而言,我讨厌他们在那里)。

(至少部分)谷歌的编码标准在某些情况下可能是有意义的,但就 C++ 而言,一般认为充其量只是平庸的建议。在某种程度上,他们甚至承认——他们公开声明的其中一些只是为了适应他们的旧代码。他们没有直接承认的其他部分,并且(说实话)这个论点无论如何都不会支持他们的一些标准(即,其中一些似乎缺乏真正的理由)。

于 2010-06-03T07:28:09.303 回答
0

这是一个很好的问题,我当然同意,如果派生类中的方法已在基类中声明为 virtual,那么在派生类中重新声明方法是一种很好的风格。虽然有一些语言在语言中构建了风格(例如 Google Go,在某种程度上还有 Python),但 C++ 并不是其中一种语言。虽然编译器当然有可能检测到派生类没有为基类中声明为“虚拟”的东西重用关键字“虚拟”(或者,更重要的是,派生类声明了一个与基类并且它没有在基类中声明为虚拟的),事实上,许多编译器都有设置在发生这种情况时发出警告(甚至错误消息)。不过在这个阶段,在语言中制定这样的要求是不切实际的,因为存在太多不那么严格的代码。此外,开发人员总是可以选择比语言更严格,并且可以调高编译器警告级别。

于 2010-06-03T07:37:24.297 回答