單例模式
- 單例類保證全局只有一個唯一實例對象。
- 單例類提供獲取這個唯一實例的接口。
(1).餓漢模式
#include<iostream>
#include <mutex>
using namespace std;
template <class T>
class EagerSingleton
{
public :
static T * GetInstance()
{
assert(_instance);
return _instance;
}
//實例銷燬(1)一般情況下,不必在乎,因爲全局就這一個變量,全局都要用,
//不知道什麼時候銷燬比較合適,一般他的生命週期就是軟件的生命週期,
//軟件結束,他就結束了(程序結束時會自動釋放它佔用的內存資源)
//(2)當在類中打開了文件句柄,數據庫連接等,需要手動釋放時,就得顯示銷燬這個實例
static void DelInstance()
{
if (_instance)
{
delete _instance;
_instance = NULL ;
}
}
//RAII 自動回收實例對象
class GC
{
public :
~GC()
{
DelInstance();
}
};
protected :
EagerSingleton();
EagerSingleton( const EagerSingleton & s);
EagerSingleton & operator = (const EagerSingleton& s);
protected :
static T * _instance;
};
template <class T>
T * EagerSingleton <T>::_instance = new T ;
(2).懶漢模式
template <class T>
class LazySingleton
{
public :
T * GetInstance()
{
// 使用雙重檢查,提高效率,避免高併發場景下每次獲取實例對象都進行加鎖
if (_instance == NULL )
{
mtx.lock();
if (_instance == NULL )
{
//下面的tmp = new LazyEagerSingleton();編譯器可能會進行優化
//上面的代碼分爲三個部分:1.分配空間 2.調用構造函數 3. 賦值
//編譯器優化可能導致2,3重排。這樣可能導致高併發場景下,
//其他線程獲取到未調用構造函數初始化的對象。解決:加入內存柵欄
//就相當於,當程序中已存在一個未調用構造函數初始化的對象,時間片到了,
//其他線程以爲對象已創建好就直接用了,對吧
T * tmp = new T;
MemoryBarrier();
_instance = tmp;
}
mtx.unlock();
}
return _instance;
}
protected :
//構造函數定義爲私有,限制只能在類內創建對象
LazySingleton();
LazySingleton( const LazySingleton & s);
LazySingleton & operator=(const LazySingleton& s);
protected :
//1.靜態成員無須創建任何對象實例就可以訪問。
//2.靜態對象在main函數之前初始化,這時只有主線程運行,所以是線程安全的。
static T * _instance;
static mutex mtx;
};
template <class T>
T * LazySingleton <T>::_instance = NULL ;