C++ 編譯期封裝-Pimpl技術


Pimpl技術——編譯期封裝

 

Pimpl 意思為“具體實現的指針”(Pointer to Implementation),

它通過一個私有的成員指針,將指針所指向的類的內部實現數據進行隱藏,

是隱藏實現,降低耦合性和分離接口實現的一個現代 C++ 技術,並有着“編譯防火牆(compilation firewall)”的名頭。

 

Pimpl技術的基本應用

其中利用了C++11的std::unique_ptr來讓Impl指針的內存更易受控制。

此外由於聲明了析構函數,導致默認的移動構造/賦值函數不能生成,若默認行為符合自己的需求,則需顯式聲明 = default

(當只在.h里,Impl是個不完整的類型,所以無法在.h類直接 = default,而是在.h聲明,在.cpp使= default)

若需要給類提供拷貝性質的函數,需要額外花點心思處理std::unique_ptr(該智能指針不支持拷貝)。

// my_class.h
#pragma once
#include <memory>

class my_class {
    //  ... 所有的公有/保護接口都可以放在這里 ...
    my_class();
    ~my_class();
    my_class(my_class&& v);    //移動構造
    my_class& operator=(my_class&& v);    //移動賦值
private:
    class Impl; 
    std::unique_ptr<Impl> pimpl;
};
// my_class.cpp
// ...include其它要依賴的頭文件...
#include "my_class.h"

class my_class::Impl {
  // 在這里定義所有私有變量和方法(換句話說是my_class類的具體實現細節內容)
  // 現在可以改變實現,而依賴my_class.h的其他類無需重新編譯...
};

my_class::my_class():pimpl(std::make_unique<Impl>()){
    // ...初始化pimpl... 
}
my_class::~my_class() = default;
my_class::my_class(my_class&& v) = default;
my_class::my_class& operator=(my_class&& v) = default;

 

代碼示例

//View.h文件
#pragma once
#include <memory>

class View
{
public:
    View();
    ~View();
    View(View&& v);
    View& operator=(View&& v);
    void display();
private:
    class Impl;
    std::unique_ptr<Impl> pimpl; 
};
//View.cpp文件
#include <iostream>
#include <string>
#include "View.h"

/////////////////////////////////////////////////////////
//下面是View::Impl的定義,也就是體現了View類的具體實現細節

class View::Impl {
    std::string name;
public:
    Impl();
    void printName();
};

View::Impl::Impl(){
    name = "DefaultName";
}

void View::Impl::printName(){
    std::cout << "this is my name:" << name;
}

///////////////////////////////////////////////////////////
//下面是View類接口的實現

View::View():pimpl(std::make_unique<Impl>()){
}

View::~View() = default;
View::View(View&& v) = default;
View& View::operator = (View&& v) = default;

void View::display(){
    pimpl->printName();
}

 

什么時候使用Pimpl技術?

可以看到Pimpl擁有如下優點:

  • 減少依賴項(降低耦合性):其一減少原類不必要的頭文件的依賴,加速編譯;其二對Impl類進行修改,無需重新編譯原類。

  • 接口和實現的分離(隱藏了類的實現)私有成員完全可以隱藏在共有接口之外,給用戶一個間接明了的使用接口,尤其適合閉源API設計。

  • 可使用惰性分配技術:類的某部分實現可以寫成按需分配或者實際使用時再分配,從而節省資源。

Pimpl也擁有一些缺點:

  • 每個類需要占用小小額外的指針內存。

  • 每個類每次訪問具體實現時都要多一個間接指針操作的開銷,並且再使用、閱讀和調試上都可能有所不便。

可以說,在性能/內存要求不敏感(非極端底層)的領域,Pimpl技術可以有相當不錯的發揮和作用。


免責聲明!

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



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