【问题标题】:"As a rule of thumb, make all your methods virtual" in C++ - sound advice?C++ 中的“根据经验,让你的所有方法都是虚拟的”——合理的建议?
【发布时间】:2012-03-28 10:44:24
【问题描述】:

我只是偶然发现了标题中的陈述。完整的报价是:

根据经验,将所有方法设为虚拟(包括 析构函数,但不是构造函数)以避免与 省略 virtual 关键字。

我在 Wrox 的书Professional C++中找到了这个。 You can google it to check.

这有什么关系吗?我原以为您只会提供选择的扩展点,而不是默认的可扩展性。例如,a 2001 article by Herb Sutter says so。从那以后,有什么发生了巨大的变化,使相反的统治规范成为了主流吗? (请注意,我是 C++ 菜鸟,所以在过去的十年里我没有关注过这个讨论。)

【问题讨论】:

  • 我听说过几次,但没有人能够说服我遵循它。对我来说似乎有点脆弱。我倾向于同意你的分析。当然,它只会是高度主观的。
  • 在类中包含第一个虚函数不会通过强制使用 vtable 或类似的东西使内部表示复杂化吗?我会认为它是“只有在你真正需要的时候才去虚拟化”。
  • ...除非您正在设计游戏。
  • 这是一个愚蠢的建议,我建议为了更大的利益而烧掉这本书。当没有理由做某事时,你就不应该这样做。遵循“建议”也可能会遇到问题。
  • 至少在我看来,这是一个可以建设性地回答的问题。是的,最终的结论将主要是一个意见问题,但导致结论的考虑因素大多是客观的,而结论只是决定哪些最重要(在你目前的情况下)。

标签: c++ virtual


【解决方案1】:

有什么关系吗?

建议不好,这是毫无疑问的。阅读这样的内容就足以让您远离这本书及其作者。

你看,virtual 关键字表示“你可以或应该重写这个方法——它就是为此而设计的”。

对于任何不平凡的任务,我无法想象一个合理的类系统将允许用户(即其他程序员)覆盖每个派生类中的每个单独的方法。拥有只有虚拟方法的基本抽象类是正常的。然而,一旦你开始创建派生类,就没有理由在所有东西上都使用“虚拟”——有些方法不需要是可扩展的。

虚拟化意味着在任何代码点,无论调用哪个方法,您都无法确定该类会执行您想要的操作,因为有人可能重写了您的方法,在过程中破坏了它(根据根据墨菲定律,它会发生)。这将使您的代码不可靠,并且难以维护。另一个非常有趣的事情是在构造函数中调用虚方法的方式。基本上,通过遵循这个建议,你牺牲了代码的可读性/可靠性,以换取不做一个非常罕见的错字。在我的看来,这不值得。

相比之下,非虚拟方法保证无论发生什么,在这个代码点上,代码将始终按您的预期工作(不包括您尚未发现的错误)。 IE。其他人不会用损坏的替代方法代替您的方法。

该建议提醒了我一些新手程序员往往会犯的一个常见错误:他们没有开发能够解决问题的简单解决方案,而是分心并试图使代码具有通用性和可扩展性。因此,项目需要更长的时间才能完成或永远无法完成 - 因为与仅限于当前问题的本地化解决方案相比,针对每种可能情况的通用解决方案需要更多的工作/开发时间。

我建议不要遵循这个“虚拟”建议,而是坚持使用 Murphy's LawKISS principle。他们为工作得很好。但是,不能保证它们适用于其他所有人。

【讨论】:

  • 谢谢,@SigTerm。让我说我发现有问题的书 - 2011 版,针对 C++11 更新 - 否则它是对该语言非常有用的介绍。我读得很慢,同时阅读了各种资料来让我很清楚,而且我已经准备好反对 Java 中的默认可扩展性。所以这个建议让我很惊讶。 - 你在说:“另一个非常有趣的事情是在构造函数中调用虚方法的方式。” Googled this;听起来你应该避免在 ctors 中调用虚方法。
【解决方案2】:

我不同意这个原则。

过去,由于性能问题,有些人担心过度使用virtual。这在某种程度上仍然有效,但在今天的硬件上并没有太大的问题。 (请记住,现在大多数其他语言都会受到类似的惩罚。例如,400MHz 的 iPhone 2G 使用的 Objective C 会在每个函数调用上产生一个虚拟方法调用。)

我认为你应该只在方法上使用virtual,在子类中重写它似乎有用且合理。对我来说,它可以作为对其他程序员(或你未来的自己)的提示,因为“这是一个子类可以明智地自定义行为的地方”。如果替换子类中的方法会令人困惑或难以实现,请不要使用virtual

另外,对于简单的 setter 和 getter,这可能是个坏主意,因为它会抑制内联。

【讨论】:

    【解决方案3】:

    性能会有微小的损失,并且会浪费几个字节的内存。

    真正的问题是它使代码的可维护性降低,因为您对函数的说法不正确。这可能会引起很多混乱。

    【讨论】:

      【解决方案4】:

      恕我直言,对于 C++ 初学者来说,这是一个很好的经验法则。除非在非常特定的情况下(程序员所面临的情况确切地知道 virtual 关键字的权衡是什么),否则它并没有真正有害,并且是一件少考虑的事情。

      然而,就像每条经验法则一样,它过于简单化了这种情况,当人们开始思考如何与其他程序员沟通时,哪些方法可能适合或不适合重新定义,这已经超出了经验法则。

      【讨论】:

        【解决方案5】:

        不会痛的。如果您没有重新定义函数的子类,则没有什么不同。如果您正在进行大量继承并且您可能会忘记哪些类继承了哪些,我可以看到这种技术很有用。

        就我个人而言,我不会将每个类方法都设为虚拟...但这就是我。拥有virtual 的所有内容似乎会让事情看起来更加混乱,恕我直言。

        【讨论】:

        • 它会受伤。具有 >0 的虚函数会增加对象大小。它可能会掩盖作者的意图。它可能会阻止编译器内联。
        • 我经常使用的另一种语言(Python)默认情况下每个函数都是虚拟的(没有关键字。)所以使用这种方法不会那么糟糕。
        • 我明白你的意思@OliCharlesworth ...我只是说代码仍然可以正常工作。但同意...这不是最有效的方法。
        • 它会受伤。覆盖错误的方法,您将破坏所有继承的功能,这可能会导致重大错误。非虚拟方法可以防止这种情况发生。坦率地说,python 是一个不好的例子。
        猜你喜欢
        • 1970-01-01
        • 2021-09-25
        • 2014-01-05
        • 2015-09-09
        • 1970-01-01
        • 2012-05-04
        • 2010-11-01
        • 1970-01-01
        • 2010-09-24
        相关资源
        最近更新 更多