我刚刚在一些代码中发现了这一点:
class Foo {
[...]
private:
virtual void Bar() = 0;
[...]
}
这有什么目的吗?
(我正在尝试将一些代码从 VS 移植到 G++,这引起了我的注意)
我刚刚在一些代码中发现了这一点:
class Foo {
[...]
private:
virtual void Bar() = 0;
[...]
}
这有什么目的吗?
(我正在尝试将一些代码从 VS 移植到 G++,这引起了我的注意)
请参阅这篇 Herb Sutter 文章,了解您为什么要做这样的事情。
这是一个纯虚函数,恰好是私有的。这使得派生类必须实现该方法。在这种情况下酒吧。
我认为您可能会对 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); };
ISO C++ 2003 明确允许它:
§10.3 没有说明访问说明符,甚至在第二个子句中包含一个脚注,说明虚函数覆盖的上下文:
[...] 在确定是否覆盖时不考虑访问控制(第 11 条)。
该代码是完全合法的。
我将引用伟大的C++ FAQ Lite中的简短解释,它总结得很好:
[23.4] 什么时候应该有人使用私有虚拟?
几乎从不。
受保护的虚拟机是可以的,但私有虚拟机通常是净损失。原因:私有虚拟会混淆新的 C++ 程序员,混淆会增加成本、延迟进度并降低风险。
新的 C++ 程序员对私有虚拟感到困惑,因为他们认为私有虚拟不能被覆盖。毕竟,派生类无法访问其基类中的私有成员,那么他们问,它如何覆盖其基类中的私有虚拟?上面有解释,但那是学术性的。真正的问题是,几乎每个人在第一次遇到私人虚拟时都会感到困惑,而困惑是不好的。
除非有相反的令人信服的理由,否则请避免使用私有虚拟。
同时更新了 C++ FAQ Lite:
顺便说一句,私有虚拟可以被覆盖,这让大多数新手 C++ 程序员感到困惑,更不用说是有效的了。我们都被告知基类中的私有成员在派生的类中是不可访问的,这是正确的。然而,派生类的这种不可访问性与虚拟调用机制(即派生类)没有任何关系。由于这可能会使新手感到困惑,因此 C++ FAQ 以前建议使用受保护的虚拟而不是私有虚拟。然而,私有虚拟方法现在已经足够普遍,以至于新手的困惑不再是一个问题。
通常的“学术”答案是:访问说明符和虚拟性是正交的——一个不会影响另一个。
更实际一点的答案:私有虚函数通常用于实现模板方法设计模式。在不支持私有虚函数的语言中,模板方法需要是公共的,尽管它并不是真正意义上的接口的一部分。
它是一个纯虚函数。从“Foo”派生的任何最终实现都必须实现“Bar”函数。
它使函数成为纯虚函数,而不是虚函数。
默认情况下不提供任何实现,其目的是函数实现必须由继承类指定。然而,这可以被覆盖。
您有时会看到完整的类,其中所有成员函数都以这种方式指定为纯虚函数。
这些是抽象基类,有时也称为接口类,ABC 的设计者对你说,“我现在知道如何为这个基类的所有特化实现这个功能。但是,你必须拥有所有这些是为您的专业化工作而定义的,并且您知道您的对象应该如何表现”。
编辑:糟糕,刚刚发现成员纯虚函数是私有的。(谢谢迈克尔)这会稍微改变一些事情。
当使用私有继承继承这个基类时,它会改变一些事情。基本上,基类的设计者正在说的是,当您的派生类调用基类中的非私有函数时。部分行为已委托给您在派生类中对函数的专门化。非私有成员正在做“某事”,而“某事”的一部分是通过纯虚拟基类函数调用您的实现。
所以 Foo 中的一些公共函数正在调用 Foo 中的 Bar 函数,它依赖于您将为您的特定情况提供 Bar 函数的专门实现这一事实。
Scott Meyers 将此称为“根据”实施。
顺便说一句,只是对那些也没有在问题中看到“细则”的人迅速删除的答案数量嗤之以鼻!(-:
高温高压
干杯,
它似乎服务的唯一目的是提供一个通用接口。
顺便说一句,即使一个函数被声明为私有虚拟,它仍然可以通过类实例或朋友来实现和调用。
尽管如此,这种东西通常是作为接口的,但我不这样做。