我一直在和我的同事讨论是否使用virtual关键字为重写方法添加前缀,或者仅在原始基类中添加前缀。
我倾向于使用virtual关键字为所有虚拟方法(即涉及vtable查找的方法)添加前缀。我的理由是三重的:
鉴于C ++缺少覆盖 关键字,虚拟的存在 关键字至少会通知你 该方法涉及查找和 理论上可以被覆盖 进一步的专业化,或者可能是 通过指向更高的指针调用 基类。
始终如一地使用这种风格 意味着,当你看到一个方法 (至少在我们的代码中)没有 虚拟关键字,你可以 最初假设它既不是 来自基地也不是专业的 在子类中。
如果,通过一些错误, 全部从IFoo中删除虚拟 孩子们仍然会运作 (CFooSpecialization :: DoBar会 仍然覆盖CFooBase :: DoBar, 而不是简单地隐藏它。)
正如我所理解的那样,反对这种做法的论点是,“但这种方法不是虚拟的”(我认为这种方法无效,并且来自对虚拟性的误解),并且“当我看到虚拟关键字时,我希望这意味着有人从中衍生出来,然后去寻找它们。“
假设类可能分布在多个文件中,并且有几个专业化。
class IFoo {
public:
virtual void DoBar() = 0;
void DoBaz();
};
class CFooBase : public IFoo {
public:
virtual void DoBar(); // Default implementation
void DoZap();
};
class CFooSpecialization : public CFooBase {
public:
virtual void DoBar(); // Specialized implementation
};
从风格上讲,你会从两个派生类中删除虚拟关键字吗?如果是这样,为什么? Stack Overflow的想法是什么?
答案 0 :(得分:23)
我完全同意你的理由。这是一个很好的提醒,该方法在调用时将具有动态调度语义。你的同事正在使用的“那种方法不是虚拟的”论点完全是假的。他混淆了虚拟和纯虚的概念。
答案 1 :(得分:6)
虚拟永远是虚拟 的功能。
因此,在任何情况下,如果虚拟关键字未在后续类中使用,则不会阻止函数/方法“虚拟”,即被覆盖。所以我参与的其中一个项目有以下指南,我有点喜欢:
/*virtual*/ void guiFocusEvent();
C ++ 11,使用'final'关键字和'override'
例如:void guiFocusEvent() override final;
答案 2 :(得分:4)
添加virtual
无论如何都不会产生重大影响。我倾向于喜欢它,但这确实是一个主观问题。但是,如果确保使用override
and sealed
keywords in Visual C++,则在编译时捕获错误的能力将得到显着提升。
我在PCH中包含以下几行:
#if _MSC_VER >= 1400
#define OVERRIDE override
#define SEALED sealed
#else
#define OVERRIDE
#define SEALED
#endif
答案 3 :(得分:3)
我倾向于不使用编译器允许我省略的任何语法。话虽如此,C#的部分设计(试图改进C ++)是要求将虚拟方法的覆盖标记为“覆盖”,这似乎是一个合理的想法。我担心的是,因为它是完全可选的,所以只有时间才会有人忽略它,到那时你就会养成期望覆盖被指定为“虚拟”的习惯。也许最好只是生活在语言的限制之内。
答案 4 :(得分:0)
我可以想到一个缺点: 如果未覆盖类成员函数并将其声明为虚拟,则在虚拟表中为该类定义添加一个不成功的条目。
答案 5 :(得分:0)
注意:我的回答是关于我们中的一些人仍然坚持的C ++ 03。 C ++ 11具有override
和final
个关键字,正如@JustinTime在评论中建议的那样,应该使用它们而不是以下建议。
已经有很多答案和两个相反的观点,这些观点最为突出。我想将@ 280Z28在他的回答中提到@StevenSudit的观点和@Abaha的风格指南结合起来。
我不同意@ 280Z28,除非您确定只会在Windows上使用该代码,否则不会使用Microsoft的语言扩展名。
但我确实喜欢这些关键字。那么为什么不只是为了清晰起见而使用#define-d关键字添加?
#define OVERRIDE
#define SEALED
或
#define OVERRIDE virtual
#define SEALED virtual
不同之处在于你在第3点中概述的情况下决定你想要发生什么。
3 - 如果通过一些错误将虚拟从IFoo中删除,所有子节点仍将起作用(CFooSpecialization :: DoBar仍会覆盖CFooBase :: DoBar,而不是简单地隐藏它)。
虽然我认为这是一个编程错误所以没有“修复”,你可能不应该费心去减轻它但应该确保它崩溃或以其他方式通知程序员(虽然我想不到现在一个。)
如果您选择第一个选项并且不喜欢添加#define
,那么您可以使用以下注释:
/* override */
/* sealed */
这应该适用于你想要清晰度的所有情况,因为我不认为 virtual 这个词足够清楚你想要的东西做。