Pointer to Implementor(PIMPL)
概念
最近看到PIMPL的設計, 基於好奇就看了網路上的資訊.
PIMPL主要的概念根據[1][2][3]可以分為兩點
(1)進一步的隱藏私有部分的成員變數及函數. 可以把所需的變數通通包在class Impl 裡面.
(2)對於程式需要新增/刪除成員變數的時候,由於已改成Impl的方式.可以減少自己程式重新編譯的時間以及避免影響使用這些函式庫的程式因為ABI關係也重新編譯.
程式範例
work.hpp為一個外層handle的class. 主要是透過public的函數作為一個介面來呼叫實作在Impl的Class程式.
line8~9是使用forward declaration.[4][5][6].所以必須使用pointer或reference的方式宣告Impl
1 class Handle 2 { 3 public : 4 Handle(); 5 void CallA(); 6 7 private: 8 class Impl; 9 Impl *m_Impl; 10 };
Line 4~8為Impl類別的實作. line 17則是handle類別透過CallA介面去呼叫實際實行的部分.
work.cpp1 #include <iostream> 2 #include "work.hpp" 3 4 class Handle::Impl 5 { 6 public: 7 void DoA() { std::cout << "Do A\n"; } 8 }; 9 10 Handle::Handle() 11 { 12 m_Impl = new Impl; 13 } 14 15 void Handle::CallA() 16 { 17 m_Impl->DoA(); 18 } 19 20 int main() 21 { 22 Handle test; 23 test.CallA(); 24 return 0; 25 }
當程式有需要新增私有變數或函數的時候,不需要像以往改了就要把相關的程式一併給重編.
透過PIMPL的方式,只需要修改work.cpp.並重編此部分.減少編譯時間以及影響使用此函式庫的外部程式.
如下為一個範例
// case 1: Without PIMPL // user.hpp class User { public : void Show(); private: string name; int age; ///< new variable. Need to re-compile all related source files }; //////////////////////////////////////////// // case 2: With PIMPL // user.hpp class User { public : void Show(); private: class Impl; Impl *m_Impl; }; // user.cpp class User::Impl { public: void show() ; private: string name: int age; ///< new variable. only re-compile this source files };
注意事項
這樣的方式會有幾點需要注意
1. 要注意operator = 的部分.由於採用PIMPL的方式,預設copy的方式會不如預期.所以要碼自己寫重寫,要碼阻止此動作. 阻止的方式可以把operator =()宣告在private或透過c++11的功能在後方加上 delete
2. 透過handle在呼叫Impl的間接方式,會稍微影響較能[7].
使用時機
[1]有提到兩個使用的時機.
1. 避免相關的程式需要除新編譯:
這邊我覺得若是比較底層的libray倒是有必要.總不能OS一更新或者重要的libraries一更新,導致所有程式都需要重編吧
2. 定義衝突與跨平台編譯問題:
這邊以我看的source code大都會以#define來區別不同平台(linux, windows, etc.). 至於結合PIMPL的情境倒是沒想過.可能沒遇過吧 = =
而[1]也沒有提供範例的程式. 這邊就先記著....
留言
張貼留言