pimpl (the pointer-to-implementation idiom)手法在 C++ 里已是“高手”们广泛运用的成熟方法之一,它的优点很多,诸如降低编译依赖、提高重编译速度之类的工具性优势自不待赘言,而其对“保持接口稳定性”的优点更值得称道。
It makes it possible to avoid other classes to know internal data structures and other information of the class. It also simplifies some#include
preprocessor instructions.避免别的class知道其内部的数据结构。还可以简化预编译指令。
pImpl方法是微软的Herb Sutter提出来的,该方法是为了尽量减小接口和实现之间的耦合,以避免接口改动对程序重新编译等带来的影响。简单来说,如果你的大型程序因为复杂的头文件包含关系,使得你对某头文件的某小改动可能引起的巨大编译时间成本望而生畏,那么你需要用pImpl方法来改善这种处境。
下面看两个代码示例。
1、
#include "classA.h"
class B
{
…
classA a;
};
2、
class classA;
class B
{
…
classA* pa;
};
这两个代码都是对classB的声明,可能会在我们工程的classB.h文件中。它们唯一的不同是1直接引用了classA.h头文件,并声明了classA类型的变量a;而2是通过"class classA;"这句进行classA的声明,然后声明了一个classA类型的指针变量pa,很明显,在2的classB.cpp文件中,需要添加#include "classA.h"。
我们来看看1会发生什么事情。如果你的classB.h文件又被其他头文件classC.h引用,而classC.h又被classD.h文件引用,当你的工程足够复杂,你的头文件之间的引用可能会非常杂乱,问题会随之出现,比如重复引用(幸好我们有#ifdef和#pragma once等方法帮我们解决问题),比如交叉引用(a引用b,b引用a,头疼),还有可能引用一些本不需要的头文件。如果你觉得这些问题都可以忽略,那么当你需要改动classA的声明时,不能忽略的问题来了,所有引用该文件的cpp文件都需要重新编译,对于大型工程,这个编译时间成本有时是不能忽略的。你总不能每改动一次classA,就把几乎大部分代码重新编译一次吧,对于开发和调试来说,这几乎不能忍受。
在1中,classA a语句必须要求引用classA的声明,以便给a变量分配空间;而2中的classA* pa语句则不需要知道classA的具体声明,因为任何指针所占用的空间都是一定的。这样,classB与classA之间就没有了耦合,classA的任何改动,对于classB来说都没有影响,除非classB修改了对classA调用的代码。在这里,指针起到了解耦合的作用。
pImpl方法是应该被提倡应用的,如果运用的恰当,对于工程开发、维护和编译都能起到正面作用,对于这种编码习惯,是需要我们在平时就注意培养的。