2010-06-03 72 views
13

当一个方法是在类中声明为virtual,其在派生类中重写被自动视为virtual以及和C++语言,使这个关键字virtual在这种情况下可选:为什么'virtual'对于派生类中的重写方法是可选的?

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

我的问题是:什么是否可以选择virtual

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

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

回答

10

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

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

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 

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

+0

认为你想从'NonVirtualBase'中删除'= 0' :) – 2010-06-03 07:30:49

+0

@Matthieu - 很棒!我今天很昏昏欲睡xD – 2010-06-03 07:32:50

2

由于语言无法强制执行“好”的风格,C++一般甚至没有尝试。至少国际海事组织(IMO),在任何情况下(包括个人,我讨厌他们在那里)是否包括这样的冗余说明都是很好的问题。

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

+0

这不是语言“不能”,而是它反对C++哲学。编译器可以检测各种风格的违规行为,包括是否在派生类中重用“虚拟”。 – 2010-06-03 07:38:24

+0

@迈克尔:或许我说得不好,但我的意图是说它不能强制一般的风格,所以它经常不会尝试,即使在明显可能的情况下(强制某些人认为是好风格的东西)。 – 2010-06-03 07:41:51

3

弱一点,我同意。 我也觉得会是真的很好,如果有两个不同的事情不同的语法:

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

当重写一个函数时使用当前的C++规则 - 这很容易搞砸了。如果你误输入函数名(或者在参数列表中输入错误) - 那么你实际上是(1)而不是(2)。

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

+1

Object Pascal正确的东西之一... – 2010-06-03 07:37:20

+1

同意,我认为它源于Bjarne Stroustrup和委员会希望尽可能小的一组关键字...因此重复使用相同的为不同的事情。同样的道理,我真的不明白,在发生阴影时不会有警告:/ – 2010-06-03 07:55:56

0

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

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]]属性的成员中指定。它使你的代码变得更加冗长,但是它在编译时可以捕捉到很多令人讨厌的bug,就像如果你做了void Vritual()而不是void Virtual(),并且当你打算重写一个现有的函数时最终引入了一个全新的函数。

相关问题