今天看到CSDN的一個討論帖:討論如何隱藏DLL標頭檔細節的作法。http://bbs.csdn.net/topics/390414874
說實話,我之前也有過類似的疑問,也沒有特別好的想法,後來工作一直沒有遇到過這個需求,就漸漸忘記了,其實某一次在miko的blog裡面看到過pimpl的說法,但是還沒有深刻理解,今天通過這個問題和查閱一些資料,終於瞭解了。下面的觀點基本上都是翻譯自[1],似乎在《Effective C++》中有(目前這本書我還沒讀過,積弱啊),我只是做了簡單概括和備份,詳細瞭解還是去[1]這個網址看看吧。
PIMPL應該是Pointer to IMPLementation的縮寫,意指指向實作類別的指標,一個簡單的代碼例子:
// XImpl.hclass XImpl {public: int fun();private: int m_data;};// XImpl.cpp#include "XImpl.h"int XImpl::fun() { return m_data * 2; }// X.hclass XImpl; // forword declarationclass 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