單例模式(Singleton)
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
代碼:https://github.com/duyilong/Design-pattern/tree/master/Singleton
- 保證僅有一個實例:不允許隨意new,將構造函數、複製構造函數、賦值運算符聲明爲私有private,注意,析構函數也要聲明爲私有。
- 全局訪問點:靜態的GetInstance方法、靜態的DeleteInstance方法。
根據instance創建的時間分爲餓漢式和懶漢式兩種實現方式。
餓漢式:the Singleton instance is EARLY createed at compile time。
懶漢式:the Singleton instance is LAZILY created at runtime。
先看代碼,
1. 線程安全單例類的懶漢式實現
#include <mutex> //std::mutex
class Singleton_lazy
{
public:
static Singleton_lazy *&GetInstance() ; //注意返回值類型,對一個指針變量的引用
static void DeleteInstance();
private:
Singleton_lazy() {}
~Singleton_lazy() {}
Singleton_lazy(const Singleton_lazy &singleton) {}
const Singleton_lazy operator=(const Singleton_lazy &singleton) {}
private:
static Singleton_lazy *m_instance;
static std::mutex m_mutex;
};
// 初始化靜態成員變量
Singleton_lazy *Singleton_lazy::m_instance = NULL;
std::mutex Singleton_lazy::m_mutex;
// 成員函數實現
Singleton_lazy *&Singleton_lazy::GetInstance()
{
if (m_instance == NULL) //雙重鎖定,防止每次調用GetInstance都要加鎖,只有發現需要實例化的時候才加鎖
{
//如果兩個線程同時通過了第一重的m_instance==NULL判斷,就會有一個先加鎖然後創建出來m_instance。
//所以還需要第二重判斷m_instance==NULL,防止被實例化出多個對象
std::unique_lock<std::mutex> lock(m_mutex); //加鎖
if (m_instance == NULL) //
{
m_instance = new Singleton_lazy;
}
}
return m_instance;
}
void Singleton_lazy::DeleteInstance()
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_instance)
{
delete m_instance;
m_instance = NULL;
}
}
注意:
- GetInstance方法的返回類型。對一個指針變量的引用,可行;指向引用的指針,不對。
- GetInstance方法的雙重鎖定。
- 在類的聲明體外對static成員變量進行定義和初始化。(注意:對於一般的類,可以只在類體外對static成員定義,然後在構造函數中初始化。注意區分聲明、定義和初始化的區別。聲明 [聲明語句爲類體內的static std::mutex m_mutex] 不會給變量分配內存空間,定義 [定義語句爲類體外的std::mutex Singleton_lazy::mutex; 對instance變量的定義和初始化在同一個語句中] 纔會給變量分配內存空間。)
2.C++11線程安全單例類的懶漢式實現
class Single
{
public:
// 獲取單實例對象
static Single &GetInstance()
{
// c++11內部靜態變量具有線程安全性,所以可以利用這個特性很簡單就實現一個線程安全的單例類
// 局部靜態特性的方式實現單實例
static Single instance;
return instance;
}
private:
Single(); // 禁止外部構造
~Single(); // 禁止外部析構
Single(const Single &signal); // 禁止外部複製構造
const Single &operator=(const Single &signal); // 禁止外部賦值操作
};
3.單例類的餓漢式實現(天生線程安全)
餓漢式利用語言運行時庫的“靜態初始化”特性,在單例類的實現代碼一加載到程序中的時候,就在內存中實例化出了一個單例類的實例。
// 單例類餓漢式實現
// 本身具有線程安全性
#include <iostream>
using namespace std;
namespace myspace
{
class singleton_early
{
public:
static singleton_early *GetInstance()
{
return g_psingle;
}
static void DeleteInstance()
{
if (g_psingle)
{
delete g_psingle;
g_psingle = NULL;
}
}
void test()
{
printf("test early singleton\n");
}
private:
singleton_early() {}
~singleton_early() {}
singleton_early(const singleton_early &single) {}
const singleton_early &operator=(const singleton_early &single) {}
private:
static singleton_early *g_psingle;
};
// 靜態成員變量的初始化
singleton_early *singleton_early::g_psingle = new singleton_early;
} // namespace myspace