上一篇文檔,PIMPL(一)
1 如何使用PIMPL
有多種方式實現PIMPL,這里按照《Effective C++》中介紹的方式。
1.1 基本步驟
假設原有Person如下:
Person.h
struct Person { public: Person(const int _age); void print(); private: int age; };
Person.cc
Person::Person(const int _age) : age(_age) {} void Person::print() { std::cout << "Person::print::age=" << age << std::endl; }
1.1.1 將Person改名為PersonImpl
PersonImpl.h
struct PersonImpl { public: PersonImpl(const int _age); void print(); private: int age; };
PersonImpl.cc
PersonImpl::PersonImpl(const int _age) : age(_age) {} void PersonImpl::print() { std::cout << "PersonImpl::print::age=" << age << std::endl; }
1.1.2 抽象public和protected方法
將PersonImpl中的public和protected方法成Person。Person中的方法實際調用的是PersonImpl中對應的方法,Person的定義中需要使用PersonImpl,在Person.h文件中絕對不能#include “PersonImpl.h”,這樣就是做無用功了。
- 為什么不能將PersonImpl的對象作為Person的成員變量?
因為Person類的定義中需要知道該類的大小,如果直接使用PersonImpl的對象,那么就必須知道PersonImpl的定義,而我們恰恰希望在Person的定義中隱藏PersonImpl的定義。這時候,指針就大顯神通了。因為指針的大小只與操作系統的位數有關(32位的機器都占4個字節,64位的機器都占8個字節),所以我們可以使用指針指向PersonImpl,從而只需要前置聲明就可以了。這就是暗度陳倉吧。
Person.h
struct PersonImpl; // 前置聲明,PIMPL的關鍵 struct Person { public: Person(const int _age); void print(); private: std::shared_ptr<PersonImpl> pImpl; // 指針,暗度陳倉 };
Person.cc
Person::Person(const int _age) : pImpl(new PersonImpl(_age)) {} void Person::print() { pImpl->print(); }
1.1.3 使用Person
在其它地方我們就可以使用Person了,例如:
int main() { Person p(1); p.print(); return 0; }
1.1.4 示例源碼下載
2 優缺點
2.1 優點
- 改變類的私有成員無需重新編譯依賴它的文件,所以整個工程重新編譯的速度會快很多。
- 頭文件中采用聲明式,因此編譯時間會快很多。
- 接口與實現的分離,更有利於單元測試。
2.2 缺點
- 實現者需要做更多的工作。
- 代碼會變得不易理解。
- 運行時動態的獲取要執行的方法,性能降低。
- 需要注意定義拷貝構造換函數或將其禁用。
3 總結
如《Effective C++》中所說:
- 如果使用object reference或object pointers可以完成任務,就不要使用objects。
- 盡量以class聲明式替換class定義式。
4 參考
- 《effective C++》 條款31:將文件間的編譯關系降至最低
- PIMPL Idiom: http://c2.com/cgi/wiki?PimplIdiom