我想我会添加一个答案,因为尽管一些作者暗示了这一点,但我认为这一点还不够清楚。
PIMPL 的主要目的是解决 N*M 问题。这个问题在其他文献中可能有其他名称,但简要总结如下。
你有某种继承层次结构,如果你要向你的层次结构添加一个新的子类,它需要你实现 N 或 M 个新方法。
这只是一个近似的手动解释,因为我最近才意识到这一点,所以我自己承认我还不是这方面的专家。
现有观点的讨论
然而,几年前我遇到了这个问题以及类似的问题,我对给出的典型答案感到困惑。(大概几年前我第一次了解 PIMPL 并发现这个问题和其他类似的问题。)
- 启用二进制兼容性(在编写库时)
- 减少编译时间
- 隐藏数据
考虑到上述“优势”,我认为它们都不是使用 PIMPL 的特别令人信服的理由。因此我从来没有使用过它,我的程序设计也因此受到影响,因为我放弃了 PIMPL 的实用性以及它真正可以用来完成的事情。
请允许我对每个评论进行解释:
1.
二进制兼容性仅在编写库时才有意义。如果您正在编译最终的可执行程序,那么这无关紧要,除非您使用的是其他人的(二进制)库。(换句话说,您没有原始源代码。)
这意味着该优势的范围和效用有限。它只对编写以专有形式发布的库的人感兴趣。
2.
我个人认为这与现代无关,因为在编译时间至关重要的项目上工作很少见。也许这对 Google Chrome 的开发者很重要。可能会显着增加开发时间的相关缺点可能会抵消这一优势。我对此可能是错的,但我发现这不太可能,尤其是考虑到现代编译器和计算机的速度。
3.
我没有立即看到 PIMPL 带来的优势。通过发送头文件和二进制目标文件可以实现相同的结果。如果没有一个具体的例子摆在我面前,很难理解为什么 PIMPL 在这里是相关的。相关的“事情”是传送二进制目标文件,而不是原始源代码。
PIMPL 的实际作用:
您将不得不原谅我略微挥手的回答。虽然我不是软件设计这一特定领域的完整专家,但我至少可以告诉你一些关于它的事情。这些信息大多是从设计模式中重复的。作者称其为“桥梁模式”,又名句柄,又名身体。
本书给出了编写窗口管理器的例子。这里的关键是窗口管理器可以实现不同类型的窗口以及不同类型的平台。
例如,一个人可能有一个
- 窗户
- 图标窗口
- 具有 3d 加速功能的全屏窗口
- 其他一些花哨的窗口
- 这些是可以渲染的窗口类型
也
- 微软视窗实施
- OS X 平台实现
- Linux X 窗口管理器
- Linux 韦兰
- 这些是不同类型的渲染引擎,具有不同的操作系统调用以及可能根本不同的功能
上面的列表类似于另一个答案中给出的列表,其中另一个用户描述了编写软件,该软件应该与不同类型的硬件一起工作,例如 DVD 播放器。(我完全忘记了这个例子是什么。)
与《设计模式》一书中所写的内容相比,我在这里给出的示例略有不同。
重点是应该使用继承层次结构来实现两种不同类型的事物,但是在这里使用单一继承层次结构是不够的。(N*M 问题,复杂度就像每个项目符号列表中事物数量的平方一样,这对于开发人员来说是不可行的。)
因此,使用 PIMPL,可以分离出窗口的类型并提供指向实现类实例的指针。
所以PIMPL:
- 解决了 N*M 问题
- 解耦使用继承建模的两个根本不同的事物,以便有 2 个或更多层次结构,而不仅仅是一个整体
- 允许运行时交换确切的实现行为(通过更改指针)。这在某些情况下可能是有利的,而单个单体强制执行静态(编译时)行为选择而不是运行时行为选择
可能还有其他方法可以实现这一点,例如使用多重继承,但这通常是一种更复杂和困难的方法,至少在我的经验中是这样。