單例模式是設(shè)計模式中最簡單的形式之一。這一模式的目的是使得類的一個對象成為系統(tǒng)中的唯一實例。要實現(xiàn)這一點,可以從客戶端對其進行實例化開始。因此需要用一種只允許生成對象類的唯一實例的機制,“阻止”所有想要生成對象的訪問。使用工廠方法來限制實例化過程。這個方法應(yīng)該是靜態(tài)方法(類方法),因為讓類的實例去生成另一個唯一實例毫無意義。<來自百度百科>
簡單地說,單例模式表示的含義是某一個類, 在一個進程中只能有唯一的一個對象(實例), 并且在語法角度上進行制約.
單例模式要點
某個類只能有一個實例
單例模式的類只提供私有的構(gòu)造函數(shù),禁止使用拷貝構(gòu)造函數(shù)或者賦值運算符來創(chuàng)建新的對象
它必須自行創(chuàng)建這個實例
類定義中,含有一個該類的靜態(tài)私有對象,既保證所有的實例只有一個成員變量,那么等同于這個類只有一個對象
他必須自行向整個系統(tǒng)提供這個實例
該類提供一個靜態(tài)的公有的函數(shù)用于創(chuàng)建或獲取它本身的靜態(tài)私有對象
單例模式用途
剛開始接觸單例模式的時候,并不知道單例模式的用途是什么,但是其實使用單例模式的地方還是很多的,比如:
每臺計算機可以有若干個打印機,但只能有一個Printer Spooler, 以避免兩個打印作業(yè)同時輸出到打印機中。
一個系統(tǒng)只能有一個窗口管理器或文件系統(tǒng);
一個系統(tǒng)只能有一個計時工具或ID(序號)生成器;
如在Windows中就只能打開一個任務(wù)管理器。如果不使用機制對窗口對象進行唯一化,將彈出多個窗口,如果這些窗口顯示的內(nèi)容完全一致,則是重復對象,浪費內(nèi)存資源;如果這些窗口顯示的內(nèi)容不一致,則意味著在某一瞬間系統(tǒng)有多個狀態(tài),與實際不符,也會給用戶帶來誤解,不知道哪一個才是真實的狀態(tài)。因此有時確保系統(tǒng)中某個對象的唯一性即一個類只能有一個實例非常重要。
所以: 當實例存在多個會引起程序邏輯錯誤的時候,請使用單例模式
單例模式的優(yōu)點
單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一的實體
因為類控制了實例化過程,所以類可以靈活更改實例化過程
單例模式缺點
雖然數(shù)量很少,但是如果每次對象請求引用的時候都要檢查是否存在類的實例,仍需要一些開銷;
懶漢模式以時間換空間;
餓漢模式以空間換時間;
單例模式常見兩種形式餓漢模式
餓漢模式就是一開始就將資源加再進來,可以說是一空間換時間的一種做法,每次使用的時候直接返回就好了
//餓漢模式
class Singleton
{
protected:
Singleton(){} // 設(shè)置為保護便于被繼承
private:
Singleton(const Singleton& s) = delete; // 防拷貝(注:delete是c++11中的語法)
Singleton* operator=(const Singleton& s) = delete; // 防賦值
private:
static Singleton* p;
public:
static Singleton* getInstance();
};
Singleton* Singleton::p = new Singleton(); // 第一次常見類就給分配資源
Singleton* Singleton::getInstance()
{
return p;
}
注意: 餓漢模式不存在線程安全的問題
懶漢模式
懶漢模式就是等到實例化對象的時候才將資源加載進來,可以說是以時間換空間的做法
經(jīng)典懶漢模式(線程不安全)
class Singleton
{
protected:
Singleton(){} //將構(gòu)造函數(shù)設(shè)置為保護,便于其他類繼承
private:
Singleton(const Singleton& s) = delete; // 實現(xiàn)防拷貝
Singleton& operator=(const Singleton& s) = delete; // 實現(xiàn)防賦值
public:
static Singleton* getInstance();
private:
static Singleton* p;
};
Singleton* Singleton::p = NULL;
Singleton* Singleton::getInstance()
{
if(p == NULL)
{// 在第一次調(diào)用的時候才去new一個對象
p = new Singleton();
}
return p;
}
線程安全的懶漢模式
class Singleton
{
protected:
Singleton()
{// 初始化互斥鎖
pthread_mutex_init(&lock_,NULL);
}
private:
Singleton(const Singleton& s) = delete; // 實現(xiàn)防拷貝
Singleton& operator=(const Singleton& s) = delete; // 實現(xiàn)防賦值
public:
static pthread_mutex_t lock_;
static Singleton* getInstance();
private:
static Singleton* p; //加volatile防止編譯器過度優(yōu)化
};
pthread_mutex_t Singleton::lock_;
Singleton* Singleton::p = NULL;
Singleton* Singleton::getInstance()
{
if(p == NULL)
{
pthread_mutex_lock(&lock_);
if(p == NULL)
{
p = new Singleton();
}
pthread_mutex_unlock(&lock_);
}
return p;
}
內(nèi)部靜態(tài)變量實現(xiàn)懶漢模式
class Singleton
{
protected:
Singleton()
{
pthread_mutex_init(&lock_,NULL);
}
private:
Singleton(const Singleton& s) = delete;
Singleton* operator=(const Singleton& s) = delete;
public:
static pthread_mutex_t lock_;
static Singleton* getInstance();
};
pthread_mutex_t Singleton::lock_;
Singleton* Singleton::getInstance()
{
pthread_mutex_lock(&lock_);
static Singleton obj;
pthread_mutex_unlock(&lock_);
return &obj;
}
特點與選擇
由于要進行線程同步,所以在訪問量比較大,或者可能訪問的線程比較多時,采用餓漢實現(xiàn),可以實現(xiàn)更好的性能。這是以空間換時間。
在訪問量較小時,采用懶漢實現(xiàn)。這是以時間換空間。
面試的時候,就寫?zhàn)I漢模式吧,畢竟簡單
作者:
黑馬程序員C++培訓學院 首發(fā):
http://c.itheima.com/?v2