亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

如何在 C++ 中實現一個單例類模板

 更新時間:2020年10月28日 10:00:08   作者:始終  
這篇文章主要介紹了如何在 C++ 中實現一個單例類模板,幫助大家更好的理解和學習c++編程,感興趣的朋友可以了解下

單例模式是最簡單的設計模式之一。在實際工程中,如果一個類的對象重復持有資源的成本很高,且對外接口是線程安全的,我們往往傾向于將其以單例模式管理。

此篇我們在 C++ 中實現正確的單例模式。

選型

在 C++ 中,單例模式有兩種方案可選。

  • 一是實現一個沒有可用的公開構造函數的基類,并提供 GetInstance 之類的靜態(tài)接口,以便訪問子類唯一的對象。由于子類構造必須調用基類構造,但基類無公開構造函數可用,這使得子類對象只能由基類及基類的友元來構造,從而在機制上保證單例。
  • 二是實現一個類模板,其模板參數是希望由單例管理的類的名字,并提供 GetInstance 之類的靜態(tài)接口。這種做法的好處是希望被單例管理的類,可以自由編寫,而無需繼承基類;并且在需要的時候,可以隨時脫去單例外衣。

此篇選擇實現一個單例類模板,其形如:

template <typename T>
struct Singleton {
 static T* get();
 T* operator->() const {
 return get();
 }
};

這里重載成員訪問運算符,是為了可以實現這樣的簡寫 Singleton<T>()->func()。

顯然,單例的實現核心在于靜態(tài)成員函數 T* get()。

一個典型的錯誤實現

一個典型的錯誤實現,是使用所謂的雙重檢查(double check)。

#include <mutex>

template <typename T>
struct Singleton {
 static T* get() {
 static T* p{nullptr};
 if (nullptr == p) {
  std::lock_guard<std::mutex> lock{mtx};
  if (nullptr == p) {
  p = new T;
  }
 }
 return p;
 }
 T* operator->() const {
 return get();
 }

 private:
 static std::mutex mtx;
};

template <typename T>
std::mutex Singleton<T>::mtx;

外層的檢查,是為了避免鎖住過大的區(qū)域,從而導致鎖的競爭特別頻繁;內層的檢查,是為了確保只在別的線程沒有提前搶占鎖完成初始化工作而設計的。這種做法在 Java 下是正確的,但是在 C++ 下則沒有保證。

另外,值得一提的是,這里 p 的初始化的線程安全性,是由 C++ 標準保證的?!?C++11 之后,標準保證函數靜態(tài)成員的初始化是線程安全的;對其讀寫則不保證線程安全。

使用標準庫提供的設施

在單例的實現中,我們實際上是希望實現「執(zhí)行且只執(zhí)行一次」的語義。C++11 之后,標準庫實際已經提供了這樣的設施。其名為 std::once_flag std::call_once。它們內部利用互斥量和條件變量組合,實現這樣的語義。值得一提的是,如果執(zhí)行過程中拋出異常,標準庫的設施不認為這是一次「成功的執(zhí)行」。于是其他線程可以繼續(xù)搶占鎖來執(zhí)行函數。

我們利用標準庫設施來實現這個類模板。

#include <mutex>

template <typename T>
struct Singleton {
 static T* get() {
 static T* p{nullptr};
 std::call_once(flag, [&]() -> void {
  p = new T;
 });
 return p;
 }
 T* operator->() const {
 return get();
 }

 private:
 static std::once_flag flag;
};

template <typename T>
std::once_flag Singleton<T>::flag;

于是你可以寫出類似這樣的代碼:

#include <mutex>
#include <iostream>
#include <future>
#include <vector>

#include "singleton.h"

struct Foo {
 void address() const {
 std::lock_guard<std::mutex> lock{mtx};
 std::cout << static_cast<void*>(const_cast<Foo*>(this)) << '\n';
 }
 mutable std::mutex mtx;
};

int main() {
 Singleton<Foo>()->address();
 std::vector<std::future<void>> futs;
 for (size_t i = 0; i != 10; ++i) {
 futs.emplace_back(std::async(&Foo::address, Singleton<Foo>::get()));
 }
 for (auto& fut : futs) {
 fut.get();
 }
 return 0;
}

得到的輸出類似這樣:

$ ./a.out
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10
0x7fbc6f405a10

Bonus:需要注意的是,所有的 std::once_flag 內部共享了同一對互斥量和條件變量。因此當存在很多 std::call_once 的時候,性能會有所下降。這一點可能需要注意一下。不過,如果存在很多 std::call_once,大概也說明程序設計不合理吧……

Bonus:注意我們這里沒有釋放 p 指向的對象。這是因為 C++ 程序對靜態(tài)變量的析構順序是不確定的。如果靜態(tài)變量之間有相互依賴,析構被依賴的對象可能會導致段錯誤。因此干脆就不釋放了,這是所謂的 LeakySingleton。當然,如果你的工程當中有實現一個通用的 ExitManager,是有可能正確析構的。但考慮到還可能大量使用第三方庫,而第三方庫不可能使用你實現的 ExitManager,于是管理所有靜態(tài)變量的析構又變得不可能,于是干脆就不管它了。

如此如此,這般這般

如果你仔細讀了這篇文章,你可能會忽然意識到剛才看到了這句話:「在 C++11 之后,標準保證函數靜態(tài)成員的初始化是線程安全的;對其讀寫則不保證線程安全。」

既然如此,我們?yōu)樯哆€要費勁使用 std::once_flagstd::call_once 呢?直接利用 static hack 出一個單例類模板不就好了嗎?

template <typename T>
struct Singleton {
 static T* get() {
 static T ins;
 return &ins;
 }
 T* operator->() const {
 return get();
 }
};

以上就是如何在 C++ 中實現一個單例類模板的詳細內容,更多關于c++ 單例類模板的資料請關注腳本之家其它相關文章!

相關文章

  • C語言中的字符型數據與ASCII碼表

    C語言中的字符型數據與ASCII碼表

    這篇文章主要介紹了C語言中的字符型數據與ASCII碼表,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • 詳解C/C++中new?A與new?A()的區(qū)別

    詳解C/C++中new?A與new?A()的區(qū)別

    這篇文章主要通過一些簡單的示例為大家詳細介紹一下C/C++中new?A與new?A()的區(qū)別,文中的示例代碼簡潔易懂,快跟隨小編一起學習起來吧
    2023-07-07
  • 判斷指定的進程或程序是否存在方法小結(vc等)

    判斷指定的進程或程序是否存在方法小結(vc等)

    VC判斷進程是否存在?比如我想知道記事本是否運行,要用到哪些函數等實例,需要的朋友可以參考下
    2013-01-01
  • C語言采用文本方式和二進制方式打開文件的區(qū)別分析

    C語言采用文本方式和二進制方式打開文件的區(qū)別分析

    這篇文章主要介紹了C語言采用文本方式和二進制方式打開文件的區(qū)別分析,有助于讀者更好的理解文本文件與二進制文件的原理,需要的朋友可以參考下
    2014-07-07
  • 嵌入式QT移植的實現

    嵌入式QT移植的實現

    本文主要介紹了嵌入式QT移植的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-05-05
  • C++容器適配器的概念與示例

    C++容器適配器的概念與示例

    C++?STL(標準模板庫)是一套功能強大的?C++?模板類,提供了通用的模板類和函數,這些模板類和函數可以實現多種流行和常用的算法和數據結構,如向量、鏈表、隊列、棧,今天我們來探究一下stl容器適配器的使用吧
    2023-01-01
  • C語言實現小型工資管理系統

    C語言實現小型工資管理系統

    這篇文章主要為大家詳細介紹了C語言實現小型工資管理系統,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C語言光標旋轉與倒計時功能實現示例詳解

    C語言光標旋轉與倒計時功能實現示例詳解

    這篇文章主要為大家介紹了C語言實現光標旋轉與倒計時功能的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2021-11-11
  • C語言中二叉樹的后序遍歷詳解

    C語言中二叉樹的后序遍歷詳解

    大家好,本篇文章主要講的是C語言中二叉樹的后序遍歷詳解,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • C語言實現輸入ascii碼,輸出對應的字符方式

    C語言實現輸入ascii碼,輸出對應的字符方式

    這篇文章主要介紹了C語言實現輸入ascii碼,輸出對應的字符方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01

最新評論