API設計筆記:pimpl技巧

pimpl

pointer to implementation:指向實現的指針,使用該技巧可以避免在頭文件暴露私有細節,可以促進API接口和實現保持完全分離。

在這里插入圖片描述

Pimpl可以將類的數據成員定義為指向某個已經聲明過的類型的指針,這里的類型僅僅作為名字引入,并沒有完整地定義,因此我們可以將該類型的定義隱藏在.cpp中,這被稱為不透明指針。

下面是一個自動定時器的API,會在被銷毀時打印其生存時間。

原有api

// autotimer.h
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif#include <string>class AutoTimer
{
public:// 使用易于理解的名字創建新定時器explicit AutoTimer(const std::string& name); // xplicit避免隱式構造, 只能通過顯示(explicit)構造.// 在銷毀時定時器報告生存時間~AutoTimer();
private:// 返回對象已經存在了多久double GetElapsed() const;std::string mName;
#ifdef _WIN32DWORD mStartTime;
#elsestruct timeval mStartTime;
#endif
};

這個API的設計包含如下幾個缺點:

1、包含了與平臺相關的定義

2、暴露了定時器在不同平臺上存儲的底層細節

3、將私有成員聲明在公有頭文件中。(這是C++要求的)

設計者真正的目的是將所有的私有成員隱藏在.cpp文件中,這樣我們可以使用Pimpl慣用法了。

將所有的私有成員放置在一個實現類中,這個類在頭文件中前置聲明,在.cpp中定義,下面是效果:

autotimer.h

// autotimer.h
#include <string.h>
class AutoTimer {
public:explicit AutoTimer(const std::string& name);~AutoTimer();
private:class Impl;Impl* mImpl;
};

構造函數需要分配AutoTimer::Impl類型變量并在析構函數中銷毀。

所有私有成員必須通過mImpl指針訪問。

autotimer.cpp

// autotimer.cpp
#include "autotimer.h"
#include <iostream>
#if _WIN32
#include <windows.h>
#else 
#include <sys/time.h>
#endifclass AutoTimer::Impl 
{
public:double GetElapsed() const{
#ifdef _WIN32return (GetTickCount() - mStartTime) / 1e3;
#elsestruct timeval end_time;gettimeofday(&end_time, NULL);double t1 = mStartTime.tv_usec / 1e6 + mStartTime.tv_sec;double t2 = end_time.tv_usec / 1e6 + end_time.tv_sec;return t2 - t1;
#endif}std::string mName;
#ifdef _WIN32DWORD mStartTime;
#elsestruct timeval mStartTime;
#endif
};AutoTimer::AutoTimer(const std::string& name) : mImpl(new AutoTimer::Impl())
{mImpl->mName = name;
#ifdef _WIN32mImpl->mStartTime = GetTickCount();
#elsegettimeofday(&mImpl->mStartTime, NULL);
#endif
}AutoTimer::~AutoTimer()
{std::cout << mImpl->mName << ":took" << mImpl->GetElapsed()<< " secs" << std::endl;delete mImpl;mImpl = NULL;
}

Impl的定義包含了暴露在原有頭文件中的所有私有方法和變量。

AutoTimer的構造函數分配了一個新的AutoTimer::Impl對象并初始化其成員,而析構函數負責銷毀該對象。

Impl類為AutoTimer的私有內嵌類,如果想讓.cpp文件中的其他類或者自由函數訪問Impl的話可以將其聲明為共有的類。

// autotimer.h
#include <string.h>
class AutoTimer {
public:explicit AutoTimer(const std::string& name);~AutoTimer();class Impl;
private:Impl* mImpl;
};

如何規劃Impl類中的邏輯?

一般將所有的私有成員和私有方法放置在Impl類中,可以避免再公有頭文件中聲明私有方法。

注意事項:

不能在Impl類中隱藏私有虛函數,虛函數必須出現在公有類中,從而保證任何派生類都能重寫他們

pimpl的復制語義

在c++中,如果沒有給類顯式定義復制構造函數和賦值操作符,C++編譯器默認會創建,但是這種默認的函數只能執行對象的淺復制,這不利于類中有指針成員的類。

如果客戶復制了對象,則兩個對象指針將指向同一個Impl對象,兩個對象可能在析構函數中嘗試刪除同一個對象兩次從而導致崩潰。

下面提供了兩個解決思路:

1、禁止復制類,可以將對象聲明為不可復制

2、顯式定義復制語義

#include <string>
class AutoTimer
{
public:explicit AutoTimer(const std::string& name);~AutoTimer();
private:AutoTimer(const AutoTimer&);const AutoTimer &operator=(const AutoTimer&);class Impl;Impl* mImpl;
}

智能指針優化Pimpl

借助智能指針優化對象刪除,這里采用共享指針or作用域指針。由于作用域指針定義為不可復制的,所以直接使用它還可以省掉聲明私有復制構造和操作符的代碼。

#include <string>
class AutoTimer
{
public:explicit AutoTimer(const std::string& name);~AutoTimer();
private:class Impl;boost::scoped_ptr<Impl> mImpl;// 如果使用shared_ptr就需要自己編寫復制構造和操作符
}

Pimpl優缺點總結

優點:

1、信息隱藏

2、降低耦合

3、加速編譯:實現文件移入.cpp降低了api的引用層次,直接影響編譯時間

4、二進制兼容性:任何對于成員變量的修改對于Pimpl對象指針的大小總是不變

5、惰性分配:mImpl類可以在需要時再構造

缺點:

1、增加了Impl類的分配和釋放,可能會引入性能沖突。

2、訪問所有私有成員都需要在外部套一層mImpl→,這會使得代碼變得復雜。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/376525.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376525.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376525.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++必讀書

C必讀書 《Inside The C Object Model》 《Effective C》和《More Effective C》以及《Exceptional C》 《C面向對象高效編程(C Effective Object-Oriented Software Construction)》 《面向對象軟件構造(Object-Oriented Software Construction)》 《設計模式(Design Patterns…

python socket編程實現的簡單tcp迭代server

與c/c socket編程對照見http://blog.csdn.net/aspnet_lyc/article/details/38946915 server&#xff1a; import socketPORT 9999 BACKLOG 5 MAXLINE 1024listenfd socket.socket(socket.AF_INET,socket.SOCK_STREAM) listenfd.bind((,PORT)) listenfd.listen(BACKLOG)w…

API設計筆記:抽象基類、工廠方法、擴展工廠

文章目錄抽象基類、工廠方法擴展工廠抽象基類、工廠方法 renderer.h #ifndef UNTITLED_RENDERER_H #define UNTITLED_RENDERER_H#include <string> class IRenderer { public:virtual ~IRenderer() {}virtual bool func1(const std::string& filename) 0;virtual …

《設計模式》-責任鏈模式

責任鏈模式是一種對象的行為模式【GOF95】。在責任鏈模式里&#xff0c;很多對象由每一個對象對其下家的用而鏈起來形成一條鏈&#xff0c;請求在這個鏈上傳遞&#xff0c;直到鏈上的某一個對象決定處理此請求。 發出請求的客戶端并不知道鏈上的哪一個對象終處理這個請求&#…

ASPX的Timer位置沒放正確,導致整頁刷新,而不是UpdatePanel里的內容刷新。

提示&#xff1a;Timer應該放在UpdatePanel的ContentTemplate標簽里&#xff0c;才行。放在外面的話&#xff0c;會導致整頁刷新。轉載于:https://www.cnblogs.com/xxxteam/p/3209522.html

高性能隨機數:mt19937、uniform_int_distribution使用

// 例如要隨機獲取一個vector中的元素 // 先對vector nums進行插入數據 .... // 使用高性能隨機數 mt19937 gen; // mt19937頭文件是<random> 是偽隨機數產生器&#xff0c;用于產生高性能的隨機數 uniform_int_distribution<int> dis(0, nums.size() - 1); //uni…

【機器學習】EM最大期望算法

EM, ExpectationMaximization Algorithm, 期望最大化算法。一種迭代算法&#xff0c;用于含有隱變量(hidden variable)的概率參數模型的最大似然估計或極大后驗概率估計&#xff0c;其概率模型依賴于無法觀測的隱變量。 經常用在ML與計算機視覺的數據聚類領域。 EM應用&#xf…

ModuleNotFoundError: No module named ‘_ctypes‘報錯解決

1、python3的安裝與卸載 先刪除現有的python3 https://codeantenna.com/a/Ys0TCtmqIJ 2、關于ctypes的報錯問題解決 安裝庫后&#xff0c;重新編譯python ModuleNotFoundError: No module named _ctypeshttps://www.jianshu.com/p/69681655309b 問題解決

做一個給自己手機免費發送“天氣預報”信息的軟件

實現一個以下截圖這樣的功能&#xff01;沒錯&#xff0c;就是你手機可以收到“免費”的天氣預報短信&#xff01; 一、在做之前必須了解以下四個功能&#xff1a; 1、WebService 2、Quartz.Net&#xff08;定時任務框架&#xff09; 3、SMTP&#xff1a;簡單郵件傳輸協議,它是…

《拾牙慧者博客檢索指南》

本指南主要概括一下我的博客所涉及到的一些方面&#xff0c;以及給出每個專欄的索引&#xff0c;方便以后自己以及他人的查找相關文章。 專欄總覽《春秋招面經》《基礎技術棧》《數據庫學習筆記》《嵌入式編程經驗》《圖像處理與計算機視覺經驗》《機器學習筆記與數學》《算法與…

Android_Chronometer計時器

最近做一個項目用到Handler 和Message &#xff0c;開始時不是很明白&#xff0c;不了解其中的內部機制&#xff0c;所以開發起來有點難度&#xff0c;之后自己找了Android 時間服務 這一節的內容&#xff0c;總結了一點關于時間的知識&#xff0c;在這里大概寫一下&#xff0c…

補碼

3&#xff0e;經常使用數值編碼 因為機器數在計算時&#xff0c;假設符號位和數值位同一時候參與運算&#xff0c;則可能會產生錯誤結果&#xff1b;而假設單獨考慮符號問題&#xff0c;又會添加運算器件的實現難度。因此&#xff0c;為了使計算機可以方便地對數值進行各種算術…

置頂 | wolai博客

最近用wolai記錄筆記較多&#xff0c;這里放一下我wolai的地址&#xff0c;當然csdn這邊也會同時更文。 hanhan的博客

深入研究Clang(四) Clang編譯器的簡單分析

作者&#xff1a;史寧寧&#xff08;snsn1984&#xff09;首先我們確定下Clang編譯器的具體內容和涵蓋范圍。之前在《LLVM每日談之二十 Everything && Clang driver 》中曾經提到過&#xff0c;Clang driver&#xff08;命令行表示是clang&#xff09;和Clang前端&…

Expression Trees 參數簡化查詢

ASP.NET MVC 引入了 ModelBinder 技術&#xff0c;讓我們可以在 Action 中以強類型參數的形式接收 Request 中的數據&#xff0c;極大的方便了我們的編程&#xff0c;提高了生產力。在查詢 Action 中&#xff0c;我們可以將 Expression Trees 用作參數&#xff0c;通過自定義的…

為你的程序添加監聽器

平時在寫程序時經常會遇到監聽器&#xff0c;比如按鈕的click監聽器&#xff0c;按鍵監聽器等等。而android中的監聽器和java中的回調函數是同一個概念&#xff0c;都是在底層代碼中定義一個接口來調用高層的代碼。那么什么是回調函數呢&#xff1f;網上說的是“在WINDOWS中&am…

圖像處理

android圖像處理系列之四&#xff0d;&#xff0d;給圖片添加邊框&#xff08;上&#xff09; http://www.oschina.net/question/157182_40586 android圖像處理系列之六&#xff0d;&#xff0d;給圖片添加邊框&#xff08;下&#xff09;&#xff0d;圖片疊加 http://www.osc…

Git push 時每次都需要密碼的疑惑

2015.1.13更新&#xff1a; 在本地搭建Git服務器時&#xff0c;也是有每次操作需要密碼的情況。 是因為每次做推送動作時&#xff0c;Git需要認證你是好人。所以需要密碼。 可以在 /home/username/.ssh/authorized_keys 文件里添加你的 ssh 公鑰。一行一個。這樣就可以在你push…

ruby字符串處理

1. str"abc123"puts str[0].chr > a puts str[0] >a的ascii碼 2.中文字符串的正則表達式 文本編碼:utf-8 文件第一行&#xff1a;#encoding:urf-8 require "iconv" str"八萬"reg/(.)萬/datareg.match(str)result Iconv.i…

PHP+七牛云存儲上傳圖片代碼片段

2014年11月14日 16:37:51 第一段代碼是上傳單個圖片的,第二個是上傳多個圖片的 1 //上傳到七牛2 //單個文件3 //formname: 表單名字; pre: 圖片Url中顯示的圖片名字(也就是七牛中的key)4 public function upImage($formname, $pre)5 {6 if (empty($_FI…