当一个方法在类中声明为virtual
时,它在派生类中的覆盖也会自动被视为virtual
,而C ++语言在这种情况下使此关键字virtual
成为可选项:
class Base {
virtual void f();
};
class Derived : public Base {
void f(); // 'virtual' is optional but implied.
};
我的问题是:virtual
可选的理由是什么?
我知道编译器并不是绝对必要的,但我认为如果编译器强制执行这样的约束,开发人员将会受益。
例如,有时当我阅读其他人的代码时,我想知道一个方法是否是虚拟的,我必须追踪它的超类来确定它。一些编码标准(Google)使virtual
关键字成为所有子类的“必须”。
答案 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)
设计中的弱点,我同意。 如果两种不同的东西有不同的语法,我也认为真的很好:
在覆盖函数时使用当前的C ++规则 - 很容易搞砸。如果你错误输入了函数名(或在参数列表中出错) - 那么你实际上是(1)而不是(2)。
你没有错误/警告。只是在运行时获得惊喜。
答案 3 :(得分:2)
由于语言不能强制执行“好”的风格,C ++一般都不会尝试。至少IMO,在任何情况下包含这样的冗余说明符是否都是好的风格(个人而言,我讨厌他们在那里时)是值得怀疑的。
(至少部分)Google的编码标准可能在某些情况下有意义,但就C ++而言,通常被认为是最好的平庸建议。在某种程度上,他们甚至承认 - 他们中的一些公开陈述只是真的适合他们的旧代码。其他部分他们不直接承认,并且(完全诚实)该论点无论如何都不会支持他们的一些标准(即,其中一些似乎缺乏真正的理由)。
答案 4 :(得分:0)
这是一个很好的问题,我当然同意,如果已在基类中声明虚拟,则在派生类中重新声明虚拟方法是一种好方法。虽然有些语言可以在语言中构建风格(例如Google Go和某种程度上的Python),但C ++并不是其中一种语言。虽然编译器当然有可能检测到派生类没有为基类中声明为“virtual”的东西重用关键字“virtual”(或者更重要的是,派生类声明了一个与基类并没有在基类中声明为虚拟),实际上,许多编译器都设置了在发生这种情况时发出警告(甚至是错误消息)的设置。但是,在这个阶段,在语言中提出这样的要求是不切实际的,因为存在太多不那么严格的代码。此外,开发人员总是可以选择比语言更严格,并且可以提高编译器警告级别。