今天看到CSDN的一个讨论帖:讨论如何隐藏DLL头文件细节的作法。http://bbs.csdn.net/topics/390414874
说实话,我之前也有过类似的疑问,也没有特别好的想法,后来工作一直没有遇到过这个需求,就渐渐忘记了,其实某一次在miko的blog里面看到过pimpl的说法,但是还没有深刻理解,今天通过这个问题和查阅一些资料,终于了解了。下面的观点基本上都是翻译自[1],似乎在《Effective C++》中有(目前这本书我还没读过,积弱啊),我只是做了简单概括和备份,详细了解还是去[1]这个网址看看吧。
PIMPL应该是Pointer to IMPLementation的缩写,意指指向实现类的指针,一个简单的代码例子:
// XImpl.h class XImpl { public: int fun(); private: int m_data; }; // XImpl.cpp #include "XImpl.h" int XImpl::fun() { return m_data * 2; } // X.h class XImpl; // forword declaration class X { public: X(); int fun(); private: XImpl* m_handle; }; // X.cpp #include "XImpl.h" #include "X.h" X::X() m_handle( new XImpl() ) {} int X::fun() { return m_handle->fun(); }
优点:
1 大大降低了编译依赖(compile-dependency),进而改善重新编译速度
因为基本上不需要修改X.h,所以客户代码无需重新编译,当然最终二进制的生成还需要link,这个是必须要做的,但是相对于大项目庞大的编译时间,这点时间几乎可以忽略。(很多大项目编译以小时计,如果修改一处代码,可能就增加了几分钟编译时间)。下面的[5][6]提到了标准库中的iosfwd,可以借鉴。
注:目前主流的C++编译器流程是这样的:1 预编译阶段,将所有的#include全部展开;2 对每个源文件进行编译,生成obj;3 进行obj的连接,生成二进制结果(例如exe或dll等)。
2 隐藏数据细节
我们知道,如果X类想给客户使用,必需提供头文件声明,而类的头文件往往写下了类的member-data和member-function,其中member-function可以把实现细节放到cpp中封装成dll,但是member-data就没那么走运了。而XImpl类正好就提供了这个功能。XImpl可以全部隐藏在dll中,而对XImpl的data的任何修改,都无需体现在X类中。
缺点:
1 增加内存占用
我们知道指针会占用4或8字节(目前大部分系统),而8字节并不是一个小数字,很多64bit程序跑不过32bit就和这个指针有莫大关系。还有原文作者提到的字节对齐(memory aligned)问题,可能会增加更多的无用字节。
2 运行时间增加
指针和数组有什么区别?答案是:数组是一个常量(它不可以发生变化),而指针是一个变量,所以取指针指向的内容,就要先加载指针的值,然后把这个值看成地址,再去地址取真正的数据,也就是所谓的“解引用”(dereference)。
那么对m_handle这个指针的任何调用都相当于多了一层解引用的开销。
多一个new的开销,我们知道栈(stack)比堆(heap)的数据开辟速度快(栈只需要移动一下栈顶指针,而堆需要考虑内存碎片,多线程竞争等等),当然也可以用自己实现的new operator加类似内存池的方式降低这个消耗,详见[1]中的论述。
总结:
对于小项目或代码片段,这个方式没什么优势。但是对于编译时间已经不可忽略的大项目,并且不在代码热点区,这个方式经常被用到,[2]中提到Qt和KDE中大量使用。
程序员有很多时间都在debug,并且经常需要重新编译,如果每次rebuild都要几分钟,确实让人boring,尽量将PIMPL这个手段加入到项目中吧。
阅读材料(按重要性排序):
[1] http://www.gotw.ca/publications/mill05.htm
[2] http://en.wikipedia.org/wiki/Opaque_pointer#cite_note-4
[3] http://hi.baidu.com/yxf_coder/item/9126bfc25eb6562fa1b50a0f
[4] http://stackoverflow.com/questions/60570/why-should-the-pimpl-idiom-be-used
[5] http://www.cplusplus.com/reference/iosfwd/
[6] http://www.cnblogs.com/Solstice/archive/2011/07/17/2108715.html
[7] http://www.oschina.net/code/snippet_102081_2211