26

我一直在与我的同事讨论是否使用 virtual 关键字作为覆盖方法的前缀,或者仅在原始基类中添加前缀。

我倾向于在所有虚拟方法(即涉及 vtable 查找的方法)前面加上 virtual 关键字。我的理由有三个:

  1. 鉴于 C++ 缺少 override 关键字,virtual 关键字的存在至少通知您该方法涉及查找并且理论上可以被进一步的特化覆盖,或者可以通过指向更高基类的指针来调用。

  2. 始终使用这种风格意味着,当您看到一个没有 virtual 关键字的方法(至少在我们的代码中)时,您最初可以假设它既不是从基类派生的,也不是专门用于子类的。

  3. 如果由于某些错误,从 IFoo 中删除了 virtual,所有子级仍将起作用(CFooSpecialization::DoBar 仍将覆盖 CFooBase::DoBar,而不是简单地隐藏它)。

据我了解,反对这种做法的论点是,“但该方法不是虚拟的”(我认为这是无效的,并且源于对虚拟性的误解),以及“当我看到 virtual 关键字时,我希望意味着有人从中派生,然后去寻找他们。”

假设的类可能分布在多个文件中,并且有几个专业化。

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
};

从风格上讲,您会从两个派生类中删除 virtual 关键字吗?如果是这样,为什么?Stack Overflow 的想法是什么?

4

6 回答 6

23

我完全同意你的理由。这是一个很好的提醒,该方法在调用时将具有动态调度语义。您的同事正在使用的“该方法不是虚拟的”论点完全是虚假的。他混淆了虚拟和纯虚拟的概念。

于 2009-09-03T01:12:56.623 回答
6

一个函数曾经是一个 virtual 总是一个 virtual

因此,无论如何,如果在后续类中未使用 virtual 关键字,它不会阻止函数/方法成为“虚拟”,即被覆盖。因此,我参与的其中一个项目有以下我有点喜欢的指导方针:

  • 如果应该覆盖函数/方法,请始终使用“virtual”关键字。在接口/基类中使用时尤其如此。
  • 如果派生类应该是子类进一步明确声明可以覆盖的每个函数/方法的“虚拟”关键字。C++11 使用“覆盖”关键字
  • 如果派生类中的函数/方法不应该再次被子类化,则要注释关键字“virtual”,表明该函数/方法已被覆盖,但没有其他类再次覆盖它。这当然不会阻止某人在派生类中重写,除非该类是最终的(不可派生的),但它表明该方法不应该被重写。例如:/*virtual*/ void guiFocusEvent(); C++11,使用 'final' 关键字和 'override' 例如:void guiFocusEvent() override final;
于 2009-09-03T03:52:47.610 回答
4

无论哪种方式,添加virtual都不会产生重大影响。我倾向于更喜欢它,但这确实是一个主观问题。但是,如果您确保在 Visual C++中使用overrideandsealed关键字,您将在编译时捕获错误的能力方面获得显着提高。

我在我的 PCH 中包含以下几行:

#if _MSC_VER >= 1400
#define OVERRIDE override
#define SEALED sealed
#else
#define OVERRIDE
#define SEALED
#endif
于 2009-09-03T01:47:37.607 回答
3

我倾向于不使用编译器允许我省略的任何语法。话虽如此,C# 的部分设计(试图改进 C++)是要求将虚拟方法的覆盖标记为“覆盖”,这似乎是一个合理的想法。我担心的是,由于它是完全可选的,因此有人忽略它只是时间问题,到那时你就会养成期望覆盖被指定为“虚拟”的习惯。那么,也许最好只生活在语言的限制内。

于 2009-09-03T01:38:35.650 回答
0

我可以想到一个缺点:当一个类成员函数没有被覆盖并且你将它声明为虚拟时,你会在虚拟表中为该类定义添加一个不必要的条目。

于 2009-09-30T16:20:33.657 回答
0

注意:我的回答是关于我们中的一些人仍然坚持使用的 C++03。正如@JustinTime 在评论中建议的那样,C++11 有overrideand关键字,可能应该使用它来代替以下建议。final

已经有很多答案和两个最突出的相反意见。我想将@280Z28 在他的回答中提到的内容与@StevenSudit 的意见和@Abhay 的风格指南结合起来。

我不同意@280Z28 并且不会使用微软的语言扩展,除非你确定你只会在 Windows 上使用该代码。

但我确实喜欢这些关键字。那么,为什么不使用#define-d 关键字添加来清楚起见呢?

#define OVERRIDE
#define SEALED

或者

#define OVERRIDE virtual
#define SEALED virtual

不同之处在于您决定在您在第三点概述的情况下要发生什么。

3 - 如果由于某些错误,从 IFoo 中删除了 virtual,所有子项仍将起作用(CFooSpecialization::DoBar 仍将覆盖 CFooBase::DoBar,而不是简单地隐藏它)。

虽然我认为这是一个编程错误,所以没有“修复”,你可能甚至不应该费心去缓解它,但应该确保它崩溃或以其他方式通知程序员(尽管我想不出一个正确的现在)。

如果您选择了第一个选项并且不喜欢添加#define's,那么您可以使用以下评论:

/* override */
/* sealed */

And that should do the job for all cases where you want clarity, because I don't consider the word virtual to be clear enough for what you want it to do.

于 2014-02-17T22:40:13.407 回答