【问题标题】:Why would a virtual function be private?为什么虚函数是私有的?
【发布时间】:2010-11-11 03:52:58
【问题描述】:

我刚刚在一些代码中发现了这一点:

class Foo {
[...]
private:
    virtual void Bar() = 0;
[...]
}

这有什么目的吗?

(我正在尝试将一些代码从 VS 移植到 G++,这引起了我的注意)

【问题讨论】:

  • 对不起,到底哪部分让您感到困惑?
  • “私人”部分。
  • 我不知道人们是否赞成之前的评论,因为这听起来很滑稽,或者他们也很困惑......
  • 这个问题对我来说很有意义,尽管可能是因为我误解了其他一些概念。如果子类无法访问/覆盖私有函数,而该私有函数是虚拟的......它如何被使用?

标签: c++ private virtual-functions access-specifier overriding


【解决方案1】:

请参阅this Herb Sutter article,了解您为什么要这样做。

【讨论】:

  • 最好用自己的话解释或总结答案。分布式自然ftw。 ;)
  • 不,最好不要。我不知道 Greg 的教学技能如何,但我怀疑他们是否比 Sutters 更好。
  • 所以本质上这里的重点是强制执行模板方法模式?这样派生类可以覆盖该方法,但只有基类可以调用它?我想这很有用,但由于派生类可以创建自己的内部方法并从 Bar() 调用它,我不确定这是否符合建议。
  • Herb Sutter 在这里不是最容易阅读的,所以最好有一个摘要。但是他的话题真的很注重细节,所以我不知道这是否可能或有帮助。萨特需要一定的经验才能弄清楚他为什么是对的。
  • 他使用的主要理由是将接口(公共函数)与实现(虚拟函数)分开。要了解更多细节,您真的只是读过这篇文章 - 这是一本很棒的 IMO 读物。
【解决方案2】:

这是一个纯虚函数,恰好是私有的。这使得派生类必须实现该方法。在本例中为 Bar。

我认为您可能会感到困惑,因为这样做是为了在 C++ 中创建“接口”,而且很多时候人们认为这些是公共的。在某些情况下,您可能希望定义一个私有接口,其中公共方法使用这些私有方法以确保调用它们的顺序。 (我相信这叫做模板方法)

举个比较差的例子:)

类记录文件 { 民众: RecordFile(const std::string &filename); 无效进程(const Record &rec) { // 调用派生类函数过滤掉 // 记录这个类的派生实例 // 不关心 如果(过滤器记录(rec)) { writeRecordToFile(rec); } }; 私人的: // 如果记录很重要,则返回 true // 并且应该保留 virtual bool filterRecord(const Record &rec) = 0; 无效 writeRecordToFile(const Record &rec); };

【讨论】:

  • 我不明白,因为私有方法在派生类中不可见。派生类如何覆盖它无法以任何方式“看到”、调用或访问的方法?
  • +1 - 你为 Herb Sutter 的论点给出了实际的理由。
  • Abelenky,您已经指出了有关私有虚拟的主要混淆来源,但事实是您不必能够 调用 函数能够定义它。这让基类告诉它的后代,“给我一个函数,但我自己决定什么时候调用它。”
  • 仅供参考,您在类声明的结束大括号后缺少一个分号。 ;)
  • @RobKennedy 我希望您的评论被发布/接受作为答案,因为它是对实际发生的事情的最清晰、最简洁的解释。没有一个答案能解释得差不多。
【解决方案3】:

ISO C++ 2003 明确允许:

§10.3 没有说明访问说明符,甚至在第二个子句中包含一个脚注,说明虚函数覆盖的上下文:

[...] 访问控制(第 11 条)是 在确定时不考虑 覆盖。

代码完全合法。

【讨论】:

    【解决方案4】:

    我将引用伟大的C++ FAQ Lite 的简短解释,总结得很好:

    [23.4] 什么时候应该有人使用 private 虚拟机?

    几乎没有。

    受保护的虚拟机没问题,但是 私有虚拟通常是一个网络 失利。原因:私有虚拟混淆 新的 C++ 程序员和困惑 增加成本,延误进度,以及 降低风险。

    新的 C++ 程序员会被 私人虚拟,因为他们认为 私有虚拟不能被覆盖。 毕竟,派生类不能 访问其私有的成员 基类,所以他们问,可以吗? 从其覆盖私有虚拟 基类?有解释 以上,但那是学术性的。这 真正的问题是几乎每个人 他们第一次跑步时会感到困惑 进入私人虚拟和混乱 不好。

    除非有令人信服的理由 相反,避免使用私有虚拟。


    同时更新了 C++ FAQ Lite:

    顺便说一句,私有虚拟可以被覆盖,这让大多数新手 C++ 程序员感到困惑,更不用说是有效的了。我们都被告知基类中的私有成员在派生的类中是不可访问的,这是正确的。然而,派生类的这种不可访问性与虚拟调用机制没有任何关系,虚拟调用机制与派生类无关。由于这可能会使新手感到困惑,C++ FAQ 以前建议使用受保护的虚拟而不是私有虚拟。然而,私有虚拟方法现在已经足够普遍,新手的困惑已不再是问题。

    【讨论】:

    • 我认为克莱恩和萨特在这里不同意。请参阅 Greg Rogers 答案中的链接。
    • 这就是我不喜欢 C++ 常见问题解答的原因,它充满了有时只是愚蠢的观点,并且违背了该领域一些领先思想家定义的最佳实践。请参阅 Herb Sutters 的回答,以获得更好的解释,并提出反对意见。
    • -1:很多事情让新的 C++ 程序员感到困惑。我们是否劝阻他们不要使用这些东西。不,当然不是因为如果我们这样做,他们会留下 新的 C++ 程序员。真是一堆废话。
    • @Martin 强烈同意。常见问题解答中说的很多事情让我咬牙切齿!但我想总比没有好。
    • 我可能同意这里制作的所有 cmets,我之前不知道它的“真实”用法。但我认为对于 C++ FAQ 所针对的人来说,它提供的答案是好的。它可能缺少的是对 Sutter 文章的引用,而不是说它没用。
    【解决方案5】:

    通常的“学术”答案是:访问说明符和虚拟性是正交的 - 一个不会影响另一个。

    更实际一点的答案:私有虚函数通常用于实现Template Method 设计模式。在不支持私有虚函数的语言中,模板方法需要是公共的,尽管它并不是真正要成为接口的一部分。

    【讨论】:

    • 如果访问说明符和虚拟性是正交的,那么为什么ideone.com/JjbVOP 编译失败?虚拟函数调用在运行时解决而不是编译时,那么我在程序中做错了什么?是不是可以通过派生类覆盖基类的受保护虚方法?
    • @Destructor 该编译错误是因为您试图从main() 调用Parent 的受保护方法。
    • "在不支持私有虚函数的语言中,模板方法需要公有..." -- 你的意思是虚方法需要公有吗?
    【解决方案6】:

    它是一个纯虚函数。从“Foo”派生的任何最终实现都必须实现“Bar”函数。

    【讨论】:

    • 除了它被标记为“私人”。 OP 没有把他的问题说清楚,在我意识到它被标记为私有之前,我不得不看一两秒钟,所以 +1 否定 -1。
    【解决方案7】:

    它使函数成为纯虚函数,而不是虚函数。

    默认情况下不提供实现,其目的是函数实现必须由继承类指定。但是,这可以被覆盖。

    您有时会看到完整的类,其中所有成员函数都以这种方式指定为纯虚函数。

    这些是抽象基类,有时称为接口类,ABC 的设计者对您说,“我现在知道如何为这个基类的所有特化实现这个功能。但是,你必须为您的专业化工作定义了所有这些,并且您知道您的对象应该如何表现”。

    编辑:糟糕,刚刚发现成员纯虚函数是私有的。 (感谢 Michael)这会稍微改变一些事情。

    当使用私有继承继承这个基类时,它会改变一些事情。基本上,基类的设计者正在说的是,当您的派生类调用基类中的非私有函数时。部分行为已委托给您在派生类中对函数的专门化。非私有成员正在做“某事”,而“某事”的一部分是通过纯虚拟基类函数调用您的实现。

    因此,Foo 中的一些公共函数正在调用 Foo 中的 Bar 函数,它依赖于您将为特定情况提供 Bar 函数的专门实现这一事实。

    Scott Meyers 将此称为“实施方面”。

    顺便说一句,只是对那些也没有在问题中看到“细则”的人迅速删除的答案数量嗤之以鼻! (-:

    HTH

    干杯,

    【讨论】:

      【解决方案8】:

      它似乎服务的唯一目的是提供一个通用接口。

      顺便说一句,即使一个函数被声明为私有虚拟,它仍然可以通过类实例或朋友来实现和调用。

      尽管如此,这种东西通常是用来作为接口的,但我不这样做。

      【讨论】:

        猜你喜欢
        • 2011-04-27
        • 1970-01-01
        • 2019-10-16
        • 2015-11-12
        • 1970-01-01
        相关资源
        最近更新 更多