关于PIMPL IDIOM(Opaque Pointer)

今天看到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(); }

类的设计是为了抽象,为了被别的代码调用,所以我们可以说使用了X类的代码都是客户代码(client code),下面来讨论一下PIMPL的优点和缺点。


优点:

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值