我正在阅读 Herb Sutter 的 “Exceptional C++” 一书,在那本书中我了解了 PIMPL 习语。基本上,这个想法是为 a 的private对象创建一个结构class并动态分配它们以 减少编译时间 (并以更好的方式隐藏私有实现)。
private
class
例如:
class X { private: C c; D d; } ;
可以改为:
class X { private: struct XImpl; XImpl* pImpl; };
并且,在 .cpp 文件中,定义:
struct X::XImpl { C c; D d; };
这看起来很有趣,但我以前从未见过这种方法,无论是在我工作过的公司中,还是在我看过源代码的开源项目中。所以,我想知道这种技术是否真的在实践中使用。
我应该在任何地方使用它,还是谨慎使用?是否建议将此技术用于嵌入式系统(性能非常重要)?
所以,我想知道这种技术是否真的在实践中使用?我应该在任何地方使用它,还是谨慎使用?
当然是用的。我在我的项目中使用它,几乎在每一堂课中。
在开发库时,您可以添加/修改字段,XImpl而不会破坏与客户端的二进制兼容性(这意味着崩溃!)。由于向X类添加新字段时类的二进制布局不会改变Ximpl,因此在次要版本更新中向库添加新功能是安全的。
XImpl
X
Ximpl
当然,您也可以在不破坏二进制兼容性的情况下向X/添加新的公共/私有非虚拟方法XImpl,但这与标准头文件/实现技术相当。
如果您正在开发一个库,尤其是专有库,最好不要透露使用了哪些其他库/实现技术来实现您的库的公共接口。要么是因为知识产权问题,要么是因为您认为用户可能会倾向于对实现做出危险的假设,或者只是通过使用可怕的铸造技巧来破坏封装。PIMPL 解决/减轻了这个问题。
编译时间减少了,因为当您向类添加/删除字段和/或方法(映射到在标准技术中添加私有字段/方法)时,只X需要重建源(实现)文件。XImpl在实践中,这是一种常见的操作。
使用标准标头/实现技术(没有 PIMPL),当您向 中添加新字段时,需要重新编译X曾经分配过的每个客户端(无论是在堆栈上还是在堆上),因为它必须调整分配的大小。X好吧,每个不分配 X 的客户端 也 需要重新编译,但这只是开销(客户端的结果代码将是相同的)。
更重要的是,即使在添加和更改XClient1.cpp私有方法时,也需要重新编译标准头/实现分离,即使出于封装原因不可能调用此方法!像上面一样,它是纯粹的开销,并且与现实生活中的 C++ 构建系统的工作方式有关。X::foo()``X``X.h``XClient1.cpp
XClient1.cpp
X::foo()``X``X.h``XClient1.cpp
当然,当您只修改方法的实现时不需要重新编译(因为您不触摸标题),但这与标准标题/实现技术相当。
是否建议将此技术用于嵌入式系统(性能非常重要)?
这取决于你的目标有多强大。然而,这个问题的唯一答案是:衡量和评估你的得失。此外,请注意,如果您不发布旨在供客户在嵌入式系统中使用的库,则仅适用于编译时间优势!