一.定義
????????單例模式是一種創建型設計模式,確保一個類只有一個實例,并提供一個全局訪問點
特點:
1.構造函數和析構函數私有化
2.禁用拷貝構造函數和賦值運算符重載(=delete)
3.利用靜態成員函數和靜態成員變量來給外界提供訪問
二.惡漢式
? ? ? ?惡漢是非常霸道的,由此可見,對于惡漢式,我們程序加載時立即創建單例實例(無論是否需要)
代碼如下:
//惡漢式:
class Singleton
{
public://利用靜態成員函數和靜態成員變量來給外界提供訪問static Singleton GetInstance(){return _instance;} //禁用拷貝構造和賦值運算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://構造析構私有化:Singleton(){}~Singleton(){}static Singleton _instance;//定義一個對象
};
//static類外實例化:
Singleton Singleton::_instance;
優點:
線程安全(C++11保證靜態變量的線程安全初始化)
程序啟動時就創建實例
簡單直接
缺點:? ? ?
本質是通過空間換來的,可能導致空間浪費
三.懶漢式
????????懶漢本質在于懶,說明只有當我們需要時才會創建單例對象,具有延遲實例化特點,通過調用GetInstance()函數來創建對象
優點;
按需創建對象,避免浪費空間
缺點:
基礎實現是非線程安全的,需額外處理多線程問題
下面我們一一來講解不同版本:
//懶漢式:
//初始版本--1
class Singleton
{
public:static Singleton* GetInstance(){if(nullptr==_instance){//創建對象_instance =new Singleton;}return _instance;}//禁用拷貝構造和賦值運算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://構造析構私有化:Singleton(){}~Singleton(){}static Singleton* _instance;//定義一個對象指針
};
//類對象實例化:
Singleton* Singleton::_instance=nullptr;
該版本問題如下:
該代碼不是線程、進程安全的,具體是指如果多個線程同時調用到GetInstance中的if語句且都進入,就會new兩個以上對象,無法確保只有一個類對象
解決方法:加鎖
//懶漢式:
//初始版本--2
#include <mutex>
class Singleton
{
public:static Singleton* GetInstance(){//單檢測法: _mutex.lock();if(nullptr==_instance){//創建對象_instance =new Singleton;}_mutex.unlock();return _instance;}//禁用拷貝構造和賦值運算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://構造析構私有化:Singleton(){}~Singleton(){}static Singleton* _instance;//定義一個對象指針static std::mutex _mutex;
};
//類對象實例化:
Singleton* Singleton::_instance=nullptr;
std::mutex Singleton::_mutex;
上面我們利用C++中提供的鎖解決了多線程問題,但是如果每次訪問都要加鎖,并且多線程訪問只有一個能夠進去,其他要等待,性能非常不好
下面我們來利用雙檢測法來解決問題:
//懶漢式:
//初始版本--3
#include <mutex>
class Singleton
{
public:static Singleton* GetInstance(){//雙檢測法: if(nullptr==_instance){_mutex.lock();if(nullptr==_instance){//創建對象 _instance =new Singleton;}_mutex.unlock();}return _instance;}//禁用拷貝構造和賦值運算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://構造析構私有化:Singleton(){}~Singleton(){}static Singleton* _instance;//定義一個對象指針static std::mutex _mutex;
};
//類對象實例化:
Singleton* Singleton::_instance=nullptr;
std::mutex Singleton::_mutex;
該雙檢測法并非是正確的雙檢測法,原因:如果CPU執行new的指令發生問題,即如果先返回對象指針,這樣就會接受到一個nullptr的指針,出現問題
newCPU執行過程;
1.分配空間 malloc
2.調用構造函數 (類)
3.返回對象指針
下面我們來學習正確的雙檢測法;
//懶漢式:
//初始版本--4
#include <mutex>
#include <atmoic>
class Singleton
{
public:static Singleton* GetInstance(){//雙檢測法: (正確寫法)Singleton* tmp = _instance.load(std::memory_order_relaxed);//std::memory_order_relaxed---C++11 引入,最寬松的內存順序約束,僅保證原子性,不提供線程間的同步或順序保證std::atomic_thread_fence(std::memory_order_acquire); if(nullptr==_instance){_mutex.lock();tmp = _instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Singleton();std::atomic_thread_fence(std::memory_order_release);_instance.store(tmp, std::memory_order_relaxed);}_mutex.unlock();}return tmp;}//禁用拷貝構造和賦值運算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://構造析構私有化:Singleton(){}~Singleton(){}static std::atomic<Singleton*> _instance;;//定義一個對象指針static std::mutex _mutex;
};
//類對象實例化:
std::atomic<Singleton*> Singleton::_instance(nullptr);
std::mutex Singleton::_mutex;
利用Atomic來保證原子性,保證雙檢測不會受到CPU執行指令順序影響
優點:
線程安全,高性能(只有第一次需要加鎖)
缺點:
實現復雜,需要注意內存屏障
最后,我們來學習發明單例模式的作者是如何寫的:
//作者實現的:
class Singleton
{
public:static Singleton& getInstance() {static Singleton instance;return instance;}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:Singleton() {}~Singleton() {}
};
特點;
線程安全(C++11保證局部靜態變量的線程安全初始化)
延遲初始化
簡潔高效
不需要考慮內存釋放問題
(只能說不愧是大佬!!!)
其實我們也可以考慮下智能指針和call_once來實現,大家可以試試
最后,感謝你的瀏覽,點個關注吧!!!