定義
單例模式(Singleton Pattern,也稱為單件模式),使用最廣泛的設(shè)計(jì)模式之一。其意圖是保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn),該實(shí)例被所有程序模塊共享。
定義一個(gè)單例類,私有化它的構(gòu)造函數(shù),以防止外界創(chuàng)建單例類的對(duì)象;使用類的私有靜態(tài)指針變量指向類的唯一實(shí)例,并用一個(gè)公有的靜態(tài)方法獲取該實(shí)例。

代碼
懶漢模式
//頭文件
class Singleton
{
public:
static Singleton& Instance() //Instance()作為靜態(tài)成員函數(shù)提供里全局訪問點(diǎn)
{
if(ps == NULL) //如果還未實(shí)例化,即可實(shí)例話,反之提供實(shí)例的引用
ps = new Singleton;
return *ps; //返回指針的話可能會(huì)誤被 delete,返回引用安全一點(diǎn)
}
private:
Singleton(); //這里將構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值函數(shù)設(shè)為私有,杜絕了生成新例
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* ps;
};
//源文件
Singleton* Singleton::ps = NULL;
這種方法的好處在于直到 Instance() 被訪問,才會(huì)生成實(shí)例,這種特性被稱為延遲初始化(Lazy Initialization),這在一些初始化時(shí)消耗較大的情況有很大優(yōu)勢(shì)。
Lazy Singleton不是線程安全的,比如現(xiàn)在有線程A和線程B,都通過了 ps == NULL 的判斷,那么線程A和B都會(huì)創(chuàng)建新實(shí)例。單例模式保證生成唯一實(shí)例的規(guī)則被打破了。
改進(jìn)的線程安全的懶漢模式
可以通過加鎖來保護(hù)單例初始化這一過程,雙檢測(cè)鎖模式就是在懶漢模式的基礎(chǔ)上稍作修改得到:
//頭文件
class Singleton
{
public:
static Singleton& Instance() //Instance()作為靜態(tài)成員函數(shù)提供里全局訪問點(diǎn)
{
if(ps == NULL)
{
Lock(); //上鎖
if(ps == NULL) //如果還未實(shí)例化,即可實(shí)例話,反之提供實(shí)例的引用
ps = new Singleton;
Unlock(); //解鎖
}
return *ps; //返回指針的話可能會(huì)誤被 delete,返回引用安全一點(diǎn)
}
private:
Singleton(); //這里將構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值函數(shù)設(shè)為私有,杜絕了生成新例
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* ps;
};
//源文件
Singleton* Singleton::ps = NULL;
上鎖和解鎖僅用于說明,實(shí)際應(yīng)用中可以使用互斥鎖,單一信號(hào)量等方法去實(shí)現(xiàn)。理論上問題解決了,但是在實(shí)踐中有很多坑,如指令重排、多核處理器等問題讓DCLP實(shí)現(xiàn)起來比較復(fù)雜比如需要使用內(nèi)存屏障
餓漢模式
//頭文件
class Singleton
{
public:
static Singleton& Instance() //Instance()作為靜態(tài)成員函數(shù)提供里全局訪問點(diǎn)
{
return instance;
}
private:
Singleton(); //這里將構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值函數(shù)設(shè)為私有,杜絕了生成新例
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton instance;
};
//源文件
Singleton Singleton::instance;
與懶漢模式相反,實(shí)例化是在初始化階段執(zhí)行的,所以沒有線程安全的問題,但是潛在問題在于no-local static對(duì)象(函數(shù)外的static對(duì)象)在不同編譯單元(可理解為cpp文件和其包含的頭文件)中的初始化順序是未定義的。如果在初始化完成之前調(diào)用 Instance()方法會(huì)返回一個(gè)未定義的實(shí)例。
優(yōu)雅的單例模式實(shí)現(xiàn)
//頭文件
class Singleton
{
public:
static Singleton& Instance() //Instance()作為靜態(tài)成員函數(shù)提供里全局訪問點(diǎn)
{
static Singleton instance;
return instance;
}
private:
Singleton(); //這里將構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值函數(shù)設(shè)為私有,杜絕了生成新例
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
};
使用local static對(duì)象(函數(shù)內(nèi)的static對(duì)象)。當(dāng)?shù)谝淮卧L問 Instance() 方法時(shí)才創(chuàng)建實(shí)例。該實(shí)現(xiàn)是線程安全的
借助pthread_once() 函數(shù)實(shí)現(xiàn)單例模式
在多線程編程環(huán)境下,盡管 pthread_once() 調(diào)用會(huì)出現(xiàn)在多個(gè)線程中,init_routine()函數(shù)僅執(zhí)行一次,pthread_once是很適合用來實(shí)現(xiàn)線程安全單例。(pthread_once 在一個(gè)進(jìn)程里只會(huì)執(zhí)行一次,其實(shí)現(xiàn)方式使用的就是互斥鎖+條件變量的方法)
//頭文件
pthread_once_t once = PTHREAD_ONCE_INIT;
class Singleton
{
public:
static Singleton& Instance() //Instance()作為靜態(tài)成員函數(shù)提供一次實(shí)例化以及全局訪問點(diǎn)
{
pthread_once(&once, &Init);
return *ps;
}
static void Init()
{
ps = new Singleton;
}
private:
Singleton();
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* ps;
};
//源文件
Singleton* Singleton::ps = NULL;
單例模式的實(shí)現(xiàn)方法很多,要完成一個(gè)完美的實(shí)現(xiàn)很難,代碼也會(huì)很復(fù)雜,但是掌握基礎(chǔ)的實(shí)現(xiàn)還是很必要的,文中提到的優(yōu)雅的單例模式和借助pthread_once() 函數(shù)實(shí)現(xiàn)單例模式是相對(duì)比較完善的方法。