为什么'virtual'是派生类中重写方法的可选项?

时间:2010-06-03 07:19:13

标签: c++ virtual derived-class

当一个方法在类中声明为virtual时,它在派生类中的覆盖也会自动被视为virtual,而C ++语言在这种情况下使此关键字virtual成为可选项:

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

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

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

例如,有时当我阅读其他人的代码时,我想知道一个方法是否是虚拟的,我必须追踪它的超类来确定它。一些编码标准(Google)使virtual关键字成为所有子类的“必须”。

5 个答案:

答案 0 :(得分:11)

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

然而,没有它就有一个技巧是不可能的:

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

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

答案 1 :(得分: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()并最终在你想要覆盖时引入一个全新的函数现有的。

答案 2 :(得分:3)

设计中的弱点,我同意。 如果两种不同的东西有不同的语法,我也认为真的很好:

  1. 声明虚拟功能。即可以在派生类中重写的函数。 (这实际上在vtable中添加了一个新的函数条目。)
  2. 覆盖派生类中的虚函数。
  3. 在覆盖函数时使用当前的C ++规则 - 很容易搞砸。如果你错误输入了函数名(或在参数列表中出错) - 那么你实际上是(1)而不是(2)。

    你没有错误/警告。只是在运行时获得惊喜。

答案 3 :(得分:2)

由于语言不能强制执行“好”的风格,C ++一般都不会尝试。至少IMO,在任何情况下包含这样的冗余说明符是否都是好的风格(个人而言,我讨厌他们在那里时)是值得怀疑的。

(至少部分)Google的编码标准可能在某些情况下有意义,但就C ++而言,通常被认为是最好的平庸建议。在某种程度上,他们甚至承认 - 他们中的一些公开陈述只是真的适合他们的旧代码。其他部分他们不直接承认,并且(完全诚实)该论点无论如何都不会支持他们的一些标准(即,其中一些似乎缺乏真正的理由)。

答案 4 :(得分:0)

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