日韩av无码中文字幕,国产午夜亚洲精品国产成人小说,成人影院午夜男女爽爽爽,欧美 亚洲 中文 国产 综合

首頁 熱點(diǎn) 要聞 國內(nèi) 產(chǎn)業(yè) 財(cái)經(jīng) 滾動(dòng) 理財(cái) 股票

C++ 單例模式的各種坑及最佳實(shí)踐

2023-06-11 21:21:35 來源 : 博客園

單例模式是設(shè)計(jì)模式中最簡(jiǎn)單、常見的一種。其主要目的是確保整個(gè)進(jìn)程中,只有一個(gè)類的實(shí)例,并且提供一個(gè)統(tǒng)一的訪問接口。常用于 Logger 類、通信接口類等。

基本原理

限制用戶直接訪問類的構(gòu)造函數(shù),提供一個(gè)統(tǒng)一的 public 接口獲取單例對(duì)象。


(相關(guān)資料圖)

這會(huì)有一個(gè)“先有雞還是先有蛋”的問題:

因?yàn)橛脩魺o法訪問構(gòu)造函數(shù),所以無法創(chuàng)建對(duì)象因?yàn)闊o法創(chuàng)建對(duì)象,所以不能調(diào)用普通的 getInstance() 方法來獲取單例對(duì)象

解決這個(gè)問題的方法很簡(jiǎn)單,將 getInstance() 定義為 static 即可(這也會(huì)限制 getInstance() 內(nèi)只能訪問類的靜態(tài)成員)

注意事項(xiàng)所有的構(gòu)造函數(shù)是 private 的拷貝構(gòu)造、拷貝賦值運(yùn)算符需要顯式刪除 =delete,防止編譯器自動(dòng)合成(即使你顯式定義了析構(gòu)函數(shù)或拷貝構(gòu)造/拷貝賦值運(yùn)算符,編譯器依然可能合成拷貝賦值運(yùn)算符/拷貝構(gòu)造!新的 C++ 標(biāo)準(zhǔn)已將該行為標(biāo)記為 deprecated,但為了兼容舊代碼,目前 C++23 仍然會(huì)合成!后面打算單獨(dú)用筆記總結(jié)一下編譯器默認(rèn)合成的函數(shù))C++ 單例模式的幾種實(shí)現(xiàn)方式版本 1 餓漢式

提前創(chuàng)建單例對(duì)象

class Singleton1 {   public:    static Singleton1* getInstance() { return &inst; }    Singleton1(const Singleton1&) = delete;    Singleton1& operator=(const Singleton1&) = delete;   private:    Singleton1() = default;    static Singleton1 inst;};Singleton1 Singleton1::inst;

這個(gè)版本在程序啟動(dòng)時(shí)創(chuàng)建單例對(duì)象,即使沒有使用也會(huì)創(chuàng)建,浪費(fèi)資源。

版本 2 懶漢式

版本 2 通過將單例對(duì)象的實(shí)例化會(huì)推遲到首次調(diào)用 getInstance(),解決了上面的問題。

class Singleton2 {   public:    static Singleton2* getInstance() {        if (!pSingleton) {            pSingleton = new Singleton2();        }        return pSingleton;    }    Singleton2(const Singleton2&) = delete;    Singleton2& operator=(const Singleton2&) = delete;   private:    Singleton2() = default;    static Singleton2* pSingleton;};Singleton2* Singleton2::pSingleton = nullptr;
版本 3 線程安全

在版本 2 中,如果多個(gè)線程同時(shí)調(diào)用 getInstance() 則有可能創(chuàng)建多個(gè)實(shí)例。

class Singleton3 {   public:    static Singleton3* getInstance() {        lock_guard lck(mtx);        if (!pSingleton) {            pSingleton = new Singleton3();        }        return pSingleton;    }    Singleton3(const Singleton3&) = delete;    Singleton3& operator=(const Singleton3&) = delete;   private:    Singleton3() = default;    static Singleton3* pSingleton;    static mutex mtx;};Singleton3* Singleton3::pSingleton = nullptr;mutex Singleton3::mtx;

加鎖可以解決線程安全的問題,但版本 3 的問題在于效率太低。每次調(diào)用 getInstance() 都需要加鎖,而加鎖的開銷又是相當(dāng)?shù)母甙旱摹?/p>版本 4 DCL (Double-Checked Locking)

版本 4 是版本 3 的改進(jìn)版本,只有在指針為空的時(shí)候才會(huì)進(jìn)行加鎖,然后再次判斷指針是否為空。而一旦首次初始化完成之后,指針不為空,則不再進(jìn)行加鎖。既保證了線程安全,又不會(huì)導(dǎo)致后續(xù)每次調(diào)用都產(chǎn)生鎖的開銷。

class Singleton4 {   public:    static Singleton4* getInstance() {        if (!pSingleton) {            lock_guard lck(mtx);            if (!pSingleton) {                pSingleton = new Singleton4();            }        }        return pSingleton;    }    Singleton4(const Singleton4&) = delete;    Singleton4& operator=(const Singleton4&) = delete;   private:    Singleton4() = default;    static Singleton4* pSingleton;    static mutex mtx;};Singleton4* Singleton4::pSingleton = nullptr;mutex Singleton4::mtx;

DCL 在很長一段時(shí)間內(nèi)被認(rèn)為是 C++ 單例模式的最佳實(shí)踐。但也有文章表示 DCL 的正確性取決于內(nèi)存模型,關(guān)于這部分的討論可以參考這兩篇文章:

https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.htmlhttps://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/版本 5 Meyers’ Singleton

這個(gè)版本利用局部靜態(tài)變量來實(shí)現(xiàn)單例模式。最早由 C++ 大佬、Effective C++ 系列的作者 Scott Meyers 提出,因此也被稱為 Meyers’ Singleton

"This approach is founded on C++"s guarantee that local static objects are initialized when the object"s definition is first encountered during a call to that function." ... "As a bonus, if you never call a function emulating a non-local static object, you never incur the cost of constructing and destructing the object."—— Scott Meyers

TLDR:這就是 C++11 之后的單例模式最佳實(shí)踐,沒有之一。

最簡(jiǎn)潔:不需要額外定義類的靜態(tài)成員線程安全,不需要額外加鎖沒有煩人的指針
class Singleton5 {   public:    static Singleton5& getInstance() {        static Singleton5 inst;        return inst;    }    Singleton5(const Singleton5&) = delete;    Singleton5& operator=(const Singleton5&) = delete;   private:    Singleton5() = default;};

我曾見到過有人畫蛇添足地返回單例指針,就像下面這樣

static Singleton* getInstance() {    static Singleton inst;    return &inst;}

如果沒什么正當(dāng)?shù)睦碛桑ㄎ乙矊?shí)在想不到有什么理由),還是老老實(shí)實(shí)地返回引用吧?,F(xiàn)代 C++ 應(yīng)當(dāng)避免使用裸指針,關(guān)于這一點(diǎn),我也有一篇筆記:裸指針七宗罪

關(guān)鍵詞:
相關(guān)文章

最近更新
超哥表情包_超哥 2023-06-11 20:15:27
精彩推送
超哥表情包_超哥 2023-06-11 20:15:27
霸王卸甲迅雷下載 2023-06-11 16:32:08
匯聚愛心 傳遞溫暖 2023-06-11 15:01:29