27

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

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

这有什么目的吗?

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

4

8 回答 8

23

请参阅这篇 Herb Sutter 文章,了解您为什么要做这样的事情。

于 2009-07-15T20:55:37.737 回答
10

这是一个纯虚函数,恰好是私有的。这使得派生类必须实现该方法。在这种情况下酒吧。

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

对于一个相对不好的例子:)

类记录文件
{
    上市:
       RecordFile(const std::string &filename);

       无效进程(const Record &rec)
       {
           // 调用派生类函数过滤掉
           // 记录这个类的派生实例
           // 不关心
           如果(过滤器记录(rec))    
           {
               writeRecordToFile(rec);           
           }
       };

    私人的:
       // 如果记录很重要,则返回 true
       // 并且应该保留
       virtual bool filterRecord(const Record &rec) = 0;

       无效 writeRecordToFile(const Record &rec);
};

于 2009-07-15T21:12:34.757 回答
9

ISO C++ 2003 明确允许它:

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

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

该代码是完全合法的。

于 2009-07-15T21:28:10.030 回答
6

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

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

几乎从不。

受保护的虚拟机是可以的,但私有虚拟机通常是净损失。原因:私有虚拟会混淆新的 C++ 程序员,混淆会增加成本、延迟进度并降低风险。

新的 C++ 程序员对私有虚拟感到困惑,因为他们认为私有虚拟不能被覆盖。毕竟,派生类无法访问其基类中的私有成员,那么他们问,它如何覆盖其基类中的私有虚拟?上面有解释,但那是学术性的。真正的问题是,几乎每个人在第一次遇到私人虚拟时都会感到困惑,而困惑是不好的。

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


同时更新了 C++ FAQ Lite:

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

于 2009-07-15T20:58:05.113 回答
6

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

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

于 2009-07-15T21:12:07.457 回答
2

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

于 2009-07-15T20:52:06.967 回答
1

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

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

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

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

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

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

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

Scott Meyers 将此称为“根据”实施。

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

高温高压

干杯,

于 2009-07-15T20:52:39.210 回答
0

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

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

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

于 2009-07-15T20:57:25.333 回答