單例模式
目錄
- 目的:主要解決一個全局使用的類頻繁的創建和銷燬的問題
- 實現:保證一個類只有一個實例存在,同時提供能對該實例加以訪問的全局訪問方法
- 三要素:(1)構造函數私有化;(2)靜態函數,用來獲取單例;(3)有一個靜態的自身變量成員,用來返回單例對象
- 分類:(1)懶漢式:通過單例函數來獲取單例,單例函數中創建對象時,需判斷對象是否存在;(2)餓漢式:不通過調用單例函數來獲取對象,而是直接在類外部new一個對象,即定義全局變量。
- 注意:懶漢式會出現多線程問題。解決:雙重鎖機制實現線程安全。
- 使用場景:創建的一個對象需要消耗的資源過多,比如I/O與數據庫的連接等。
1、懶漢式
//懶漢式
class LanHanShi
{
public:
static LanHanShi* GetObject()
{
if (lhs == NULL) //判斷對象是否已存在,保證只初始化一個對象
{
lhs = new LanHanShi;
/*
若在多線程中,構造函數中有sleep(100);則有多個線程時,在每個線程
創建對象進入這個函數時,會首先判斷對象是否已存在,而由於此線程
停留在構造函數中還未返回,所以對象還未創建,另一個線程也將進入構造
函數中,所以此時會創建多個對象,則不再是單例模式。
*/
}
/*解決懶漢式與多線程的問題方法:
if(lhs==NULL) //第一次檢查保證所有線程進來之前的判斷
{
cs.lock(); //可能到這一步,有好幾個線程進來
if(lhs==NULL) //第二次檢查,保證在有一個線程已進來創建對象後,
{ //防止其他線程進來再次創建對象
lhs = new LanHanShi;
}
cs.unlock(); //加鎖部分爲臨界區
}
*/
return lhs;
}
~LanHanShi()
{
if (lhs != NULL)
{
delete lhs;
lhs = NULL;
cout << "~LanHanShi()" << endl;
}
}
private:
LanHanShi()
{
cout << "LanHanShi()" << endl;
}
private:
static LanHanShi *lhs;
};
LanHanShi* LanHanShi::lhs = NULL; //靜態類型需要在外部定義
//定義格式:(不加static)類型 類名::變量名=初始化值
2、餓漢式
class EHanShi
{
public:
static EHanShi* GetObject() //函數中有靜態變量,則函數必須是靜態函數
{
return ehs;
}
private:
EHanShi()
{
cout << "EHanShi()" << endl;
}
private:
static EHanShi *ehs;
};
EHanShi* EHanShi::ehs = new EHanShi; //餓漢式直接在類外部進行new對象
//這句話是在調用函數之前就被執行,爲全局變量
void EHanShiDisplay()
{
EHanShi *p1 = EHanShi::GetObject();
EHanShi *p2 = EHanShi::GetObject();
}
void LanHanShiDisplay()
{
//懶漢式即只在GetObject執行時纔會獲取單例
LanHanShi *p1 = LanHanShi::GetObject();
LanHanShi *p2 = LanHanShi::GetObject();
//確實構造函數只執行了一次,創建了一個單例
//單例生命週期和類一樣,不會執行析構函數
}
3、測試
int main1()
{
LanHanShiDisplay();
EHanShiDisplay();
//構造函數都只被執行了一次,爲單例模式,懶漢式有多線程問題,而餓漢式沒有
system("pause");
return 0;
}
結果: