程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++的PIMPL模式解析

C++的PIMPL模式解析

編輯:關於C++

PIMPL(pointer to implementation)是一種常用的,用來對“類的接口與實現”進行解耦的方法。pimpl具有如下優點:

降低模塊的耦合 降低編譯依賴,提高編譯速度 接口與實現分離

為了實現pimpl模式,我們先來看一種普通的類的設計方法。
假如我們要設計一書籍類Book,Book包含目錄屬性,並提供打印書籍信息的對外接口,Book設計如下:

class Book
{
public:
  void print();

private:
  std::string  m_Contents;
};

Book的使用者只需要知道print()接口,便可以使用Book類,看起來一切都很美好。
然而,當某一天,發現Book需要增加一標題屬性,對Book類的修改如下:

class Book
{
public:
  void print();

private:
  std::string  m_Contents;
  std::string  m_Title;
};

雖然使用print()接口仍然可以直接輸出書籍的信息,但是Book類的使用者卻不得不重新編譯所有包含Book類頭文件的代碼。
為了隱藏Book類的實現細節,實現接口與實現的真正分離,可以使用pimpl模式。
我們依然對Book類提供相同的接口,但Book類中不再包含原有的數據成員,其所有操作都由BookImpl類實現。

/* public.h */
#ifndef PUBLIC_H_INCLUDED
#define PUBLIC_H_INCLUDED

class Book
{
public:
  Book();
  ~Book();
  void print();

private:
  class BookImpl;  // Book實現類的前置聲明
  BookImpl* pimpl;
};

#endif

為簡單實現起見,Book類省略了拷貝構造函數和拷貝賦值函數。在實際應用中,應該對這兩個函數進行定義。
在對外的頭文件public.h中,只包含Book類的外部接口,將真正的實現細節被封裝到BookImpl類。為了不對外暴露BookImpl類,將其聲明為Book類的內嵌類,並聲明為private。

BookImpl類的頭文件如下。

/* private.h */
#ifndef PRIVATE_H_INCLUDED
#define PRIVATE_H_INCLUDED

#include public.h
#include 

class Book::BookImpl
{
public:
  void print();

private:
  std::string  m_Contents;
  std::string  m_Title;
};

#endif

private.h並不需要提供給Book類的使用者,因此,如果往後需要重新設計書籍類的屬性,外界對此一無所知,從而保持接口的不變性,並減少了文件之間的編譯依賴關系。

/* book.cpp */
#include private.h  // 我們需要調用BookImpl類的成員函數,
                      // 所以要包含BookImpl的定義頭文件
#include public.h  // 我們正在實現Book類,所以要包含Book類
                     // 的頭文件

Book::Book()
{
  pimpl = new BookImpl();
}

Book::~Book()
{
  delete pimpl;
}

void Book::print()
{
  pimpl->print();
}

/* BookImpl類的實現函數 */

void Book::BookImpl::print()
{
  std::cout << print from BookImpl << std::endl;
}

使用Book類的接口的方法如下:

/* main.cpp */
#include public.h

int main()
{
    Book book;
    book.print();

    return 0;
}

像Book類這樣使用pimpl的類,往往被稱為handle class,BookImpl類作為實現類,被稱為implementation class
使用pimpl帶來的額外開銷包括,handle class成員函數的每次調用都必須通過implementation class,這會增加一層間接性,另外,每一個對象都增加了一個implementation class指針的大小。在實際中你需要對這些開銷進行權衡。

可以使用下圖來說明pimpl模式在以上Book類設計的作用:
pimpl作為類實現的編譯防火牆
由於pimpl解除了接口與實現之間的耦合關系,從而降低文件間的編譯依賴關系,pimpl也因此常被稱為“編譯期防火牆“ 。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved