c 中單例模式實現總結

2021-09-29 11:23:42 字數 4375 閱讀 5604

前言

最近個人在寫一些小的練手的專案,發現單例模式的應用實在是很廣泛,很多情況下乙個類都需要使用單例模式來實現,因此這裡我再度總結一下單例模式的實現,之前已經總結過一次,當時的例子給的是muduo的單例模式實現,這次再給出幾種其他的實現,同時進行一下比較。

餓漢模式

餓漢模式就是在還未使用變數的時候,已經對該變數進行賦值,有點類似很飢餓的感覺,在main函式開始執行前就已經初始化好了,因此是執行緒安全的。

不考慮析構函式的餓漢模式實現

#include

using

namespace std;

class

singleton

;private

:singleton

(const singleton&);

singleton&

operator=(

const singleton&);

static singleton* m_instance;

public

:static singleton*

getinstance()

;~singleton()

};singleton* singleton::

getinstance()

singleton* singleton::m_instance =

new singleton;

intmain()

餓漢模式的優點

執行緒安全

簡單易懂易實現

餓漢模式的缺點

不適合對效能有要求的場景,因為有些場景下可能不會用到這個變數,這樣就無需載入

在main函式之前就已經初始化完成,基本上沒辦法給類傳入引數

懶漢模式

這個從名字上更容易理解,懶漢就是不到萬不得已絕不行動,對於單例模式來說就是預設情況下變數不初始化為nullptr,而在getinstance方法被呼叫的時候才會判斷instance是否已經被初始化,然後再決定是否要初始化,不過這個行為導致的乙個重要問題就是執行緒不安全。

不考慮析構函式的懶漢模式實現

#include

using

namespace std;

class

singleton

;private

:singleton

(const singleton&);

singleton&

operator=(

const singleton&);

static singleton* m_instance;

public

:static singleton*

getinstance()

;~singleton()

};singleton* singleton::

getinstance()

return m_instance;

}singleton* singleton::m_instance =

null

;int

main()

解決懶漢模式執行緒不安全的幾種方法

使用區域性靜態變數

區域性靜態變數的初始化是執行緒安全的,這一點由編譯器保證.( 這是乙個 gcc 的 patch,專門解決這個問題)。會在程式退出的時候自動銷毀。不過需要注意的是,這個方法適合 c++11,c++11保證靜態區域性變數的初始化是執行緒安全的。如果是 c++98 就不能用這個方法。

class

sprivate:s

()s(s const&)

=delete

;// don't implement.

void

operator

=(s const&)

=delete

;// don't implement

};

加鎖

這個辦法應該是最簡單最直接的了,**實現如下:

// singleton.h

class

singleton

return p;

}private

: std::mutex mutex_;

static singleon *p;

singleton()

singleton

(const singleton &)=

delete

; singleton&

operator=(

const singleton &)=

delete;}

;// singleton.cpp

singleton *singleton::p =

null

;

這個方法的缺點是每次呼叫getinstance都會加鎖,在多執行緒的情況下可能會輕微增加程式的負擔。

pthread_once

這個是我在以前推薦的部落格裡面寫的,陳碩推薦的單例模式實現,是乙個非常好的實現方式,不過僅限於在linux系統下

// singleton.h

class

singleton

private

:singleton()

singleton

(const singleton &)=

delete

; singleton&

operator=(

const singleton &)=

delete

;static

void

init()

static pthread_once_t ponce_;

static singleton *value_;};

// singleton.cpp

pthread_once_t singleton::ponce_ = pthread_once_init;

singleton* singleton::value_ =

null

;

double check locking(dcl)

double check locking. 只能用記憶體屏障,其他做法都是有問題的。普通的 double check 之所以錯,是因為亂序執行和多處理器下,不同 cpu 中間 cache 刷往記憶體並對其他 cpu 可見的順序無法保障(cache coherency problem)。比如

singleton*p = new singleton;

這個語句的實際執行過程有三步:

分配記憶體

構造物件

賦值給p

由於亂序執行,因此2和3的執行順序實際上是不一定的。因此,如果在某些情況下,3先於2執行,那麼就會出現p實際上指向乙個無效的物件,但是由於p已經不再是空指標,所以在dcl的第一步判斷p是否為空指標時,順利通過,然後p被返回,然而此時的p指向的記憶體是位被構造的。

可能有同學認為可以增加乙個臨時變數temp_p,當temp_p構造完成之後再賦值給p,但是對於現代的編譯器這樣做是沒有意義的,因為現代編譯器會認為temp_p是無效的,可能會被優化掉,因此就回到了上乙個問題,因此dcl不是執行緒安全的。

自動呼叫析構函式

在前面說的懶漢模式不考慮析構函式和餓漢模式不考慮析構函式中,**中我們雖然都寫了析構函式,但是如果我們執行的話會發現,其實析構函式並不會被呼叫,這是為什麼呢,其實答案很簡單,因此我們的物件是new出來的,然而我們並沒有顯式的呼叫delete,因此析構函式自然不會被呼叫,那麼我們是需要手動呼叫析構函式嗎,其實並非如此,我們希望一種可以自動呼叫析構函式的方式來實現,其實也很簡單,就是之前說的區域性靜態變數,實現如下,其實和之前一樣:

#include

using

namespace std;

class

singleton

;private

:singleton

(const singleton&);

singleton&

operator=(

const singleton&);

public

:static singleton*

getinstance()

;~singleton()

};singleton* singleton::

getinstance()

intmain()

其實寫到這裡基本就差不多了,不過這個方法的缺點就是只有在c++11標準下才能保證其正確性,不過從目前來看,c++11的普及性已經很好了,所以基本上不怎麼需要擔心這個問題

總結其實說到這裡,單例模式就講的差不多了,陳碩推薦的pthread_once方法和c++11下靜態區域性變數的方法都是很不錯的,值得學習。

C 中實現單例模式

單例模式是軟體工程中廣為人知的設計模式。單例模式就是指乙個永遠只能例項化一次。使用的方式是呼叫類裡建立的靜態方法。通常來說,單例模式建立的類,都是不帶形參的 原因就是當建立多個例項的時候,如果引數不同的話 比如2個不同的過載建構函式 那麼就會造成一些不必要的問題 如果相同的例項要被建立而且他們使用相...

單例模式總結 C

單例模式 保證在整個程式中只有乙個例項,並提供乙個各個程式模組都可以訪問的介面。一 常用標準模式 include using namespace std class singleton int m x static singleton m instance public static singlet...

C 單例模式總結

c 單例模式總結 單例模式可以說是在開發過程中最常用的一種設計模式了,一般很多業務處理層都會實現單例模式。單例模式分為懶漢式和餓漢式,懶漢式是在呼叫的時候生成唯一的例項,餓漢式是在系統初始化的時候就實現例項。這裡有乙個有意思的問題,如何在不知道程式的時候,判斷單例是懶漢式或者是餓漢式?單例的實現是有...