單例模式淺談二
class CSingleton /* 懶漢式 */ { public: static CSingleton * GetInstance() // 寫法是在getInstance中new instance然後返回 { if(m_pInstance == NULL) //判斷是否第一次呼叫 m_pInstance = new CSingleton; return m_pInstance; } void RelaseInstance() { delete this; } private: CSingleton() //建構函式是私有的 { } CSingleton(const CSingleton& that)//拷貝建構函式也應是私有的 { } ~CSingleton() { m_pInstance = NULL; } static CSingleton *m_pInstance; };
單例模式,可以說設計模式中最常應用的一種模式了,據說也是面試官最喜歡的題目。但是如果沒有學過設計模式的人,可能不會想到要去應用單例模式,面對單例模式適用的情況,可能會優先考慮使用全域性或者靜態變數的方式,這樣比較簡單,也是沒學過設計模式的人所能想到的最簡單的方式了。
一般情況下,我們建立的一些類是屬於工具性質的,基本不用儲存太多的跟自身有關的資料,在這種情況下,每次都去new一個物件,即增加了開銷,也使得程式碼更加臃腫。其實,我們只需要一個例項物件就可以。如果採用全域性或者靜態變數的方式,會影響封裝性,難以保證別的程式碼不會對全域性變數造成影響。
考慮到這些需要,我們將預設的建構函式宣告為私有的,這樣就不會被外部所new了,甚至可以將解構函式也宣告為私有的,這樣就只有自己能夠刪除自己了。在Java和C#,QT這樣純的面向物件的語言中,單例模式非常好實現,直接就可以在靜態區初始化instance,然後通過getInstance返回,這種就被稱為餓漢式單例類。也有些寫法是在getInstance中new instance然後返回,這種就被稱為懶漢式單例類,但這涉及到第一次getInstance的一個判斷問題。
下面的程式碼只是表示一下,跟具體哪種語言沒有關係。
單執行緒中:
1 2 3 4 5 6 7 |
|
這樣就可以了,保證只取得了一個例項。但是在多執行緒的環境下卻不行了,因為很可能兩個執行緒同時執行到if (instance == NULL)這一句,導致可能會產生兩個例項。於是就要在程式碼中加鎖。
/*
多執行緒同步,一旦有個一個執行緒搶到鎖之後,(每個執行緒搶到的機會都是相等,當然也可以排程),其他執行緒就會
阻塞在門外**/
Singleton* getInstance()
{
lock(); //上鎖
if (instance == NULL)
{
instance = new Singleton();
}
unlock();//解鎖
return instance;
}
但這樣寫的話,會稍稍映像效能,因為每次判斷是否為空都需要被鎖定,如果有很多執行緒的話,就會造成大量執行緒的阻塞。再次資源浪費,於是大神們又想出了雙重鎖定。
/*
這樣就避免大量執行緒阻塞造成的資源浪費,頂多有幾個執行緒同時開始創造一個單例時,機率很小
*/
Singleton* getInstance()
{
if (instance == NULL)
{
lock();
if (instance == NULL)
{
instance = new Singleton();
}
unlock();
}
return instance;
}
這樣只夠極低的機率下,通過越過了if (instance == NULL)的執行緒才會有進入鎖定臨界區的可能性,這種機率還是比較低的,不會阻塞太多的執行緒,但為了防止一個執行緒進入臨界區建立例項,另外的執行緒也進去臨界區建立例項,又加上了一道防禦if (instance == NULL),這樣就確保不會重複建立了。
常用的場景
單例模式常常與工廠模式結合使用,因為工廠只需要建立產品例項就可以了,在多執行緒的環境下也不會造成任何的衝突,因此只需要一個工廠例項就可以了。
優點
1.減少了時間和空間的開銷(new例項的開銷)。
2.提高了封裝性,使得外部不易改動例項。
缺點
1.懶漢式是以時間換空間的方式。
2.餓漢式是以空間換時間的方式。
此博文轉載處:https://blog.csdn.net/kaida1234/article/details/79806943