PIMPL(一)


1 參考

  1. 《effective C++》 條款31:將文件間的編譯關系降至最低
  2. PIMPL Idiom: http://c2.com/cgi/wiki?PimplIdiom

2 什么是PIMPL?

  PIMPL是指pointer to implementation。通過使用指針的方式隱藏對象的實現細節。是實現“將文件間的編譯依存關系降至最低”的方法之一。另一個方式是通過接口實現,但其原理一樣。

  PIMPL又稱作“編譯防火牆”、“笑臉貓技術”,它只在C/C++等編譯語言中起作用。

3 為什么要使用PIMPL?

  3.1 理論分析

  龐大的項目,修改一個文件之后,重新編譯,所有依賴該文件的文件都需要重新編譯,導致編譯時間太長。

  3.2 工程實例

  通過描述一個實例來證明上一小節的理論。

    3.2.1 不使用PIMPL

  文件間的依賴關系如圖:

  有三個源文件依賴“Person.h”,實際中可以有更多個文件依賴它,為了說明意思,我源碼寫的都非常簡單,主要是為了表明文件間的依賴關系而已。

Person.h

#ifndef PERSON_H_
#define PERSON_H_
struct Person
{
  void print();
};
#endif Person.cc

#include "Person.h"
#include <iostream>

void
Person::print()
{
  std::cout << "Person::print()" << std::endl;
}

PersonUser.cc

#include "Person.h" main.cc
#include
"Person.h"
int main() { return 0; } Makefile
# # Makefile # author: zhaokai # date:
2013-11-28 # TESTS = main all : $(TESTS) clean : rm -f $(TESTS) rm -f main.o PersonUser.o Person.o main.o: main.cc Person.h g++ -c main.cc PersonUser.o : PersonUser.cc Person.h g++ -c PersonUser.cc
Person.o: Person.cc Person.h g
++ -c Person.cc $(TESTS): main.o PersonUser.o Person.o g++ -o main main.o PersonUser.o Person.o

   現在我們開始修改Person.h文件:

#ifndef PERSON_H_
#define PERSON_H_

struct Person
{
  int i; // add int i
  void print();
};
#endif

  然后make,結果如下:

  依賴Person.h的三個文件都被重新編譯了,最后鏈接生成執行文件。

    3.2.2 使用PIMPL

  使用PIMPL需要將Person類的實現移到PersonImpl類中,使用指針的方式將實現隱藏,相當於Person.h只是一個傀儡而已,而以前依賴它的文件依舊依賴之,文件間的依賴關系如圖:

  有三個源文件依賴“Person.h”,實際中可以有更多個文件依賴它,有兩個源文件和一個頭文件依賴“PersonImpl.h”。

Person.h

#ifndef PERSON_H_
#define PERSON_H_
#include <memory>

struct PersonImpl;
struct Person
{
  void print();
 private:
  std::shared_ptr<PersonImpl> pImpl;
};
#endif Person.cc

#include "Person.h"
#include "PersonImpl.h"

void
Person::print()
{
  pImpl->print();
}

PersonImpl.h
#ifndef PERSONIMPL_H_
#define PERSONIMPL_H_ struct PersonImpl { void print(); }; #endif PersonImpl.cc #include "PersonImpl.h" #include <iostream> void PersonImpl::print() { std::cout << "PersonImpl::print()" << std::endl; } PersonUser.cc
#include
"Person.h" main.cc
#include
"Person.h" int main() { return 0; } Makefile # # Makefile # author: zhaokai # date: 2013-11-28 # TESTS = main all : $(TESTS) clean : rm -f $(TESTS) rm -f main.o PersonUser.o Person.o PersonImpl.o main.o: main.cc Person.h g++ --std=c++11 -c main.cc PersonUser.o : PersonUser.cc Person.h g++ --std=c++11 -c PersonUser.cc Person.o: Person.cc Person.h PersonImpl.h g++ --std=c++11 -c Person.cc PersonImpl.o: PersonImpl.cc PersonImpl.h g++ --std=c++11 -c PersonImpl.cc $(TESTS): main.o PersonUser.o Person.o PersonImpl.o g++ -o main main.o PersonUser.o Person.o PersonImpl.o

  現在我們開始修改PersonImpl.h文件,注意這時候Person.h已經是傀儡了,如果想給Person增加屬性那應該修改PersonImpl.h文件:

#ifndef PERSONIMPL_H_
#define PERSONIMPL_H_

struct PersonImpl
{
  int i; // add int i
  void print();
};
#endif

  然后make,結果如下:

  依賴PersonImpl.h的兩個文件都重新編譯了,而依賴於“Person.h”的文件main.cc和PersonUser.cc都沒有重新編譯。

    3.2.3 對比

  同樣是一件事情,為Person類增加屬性int i;兩種方法導致編譯的過程就不同,我們舉得例子比較小,如果有100個類似PersonUser這樣的文件,那么使用PIMPL,編譯時還是只有“Person.cc”和“PersonImpl”兩個文件重新編譯了;但是不使用PIMPL的話,就是“main.cc”,“Person.cc”和100個類似“PersonUser.cc”這樣的文件重新編譯,那就是102個文件。

  通過上面的實例就可證明理論分析部分了。

  至於如何使用PIMPL,敬請期待下一篇文章!

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM