C++設計模式之單例模式詳解(懶漢模式、餓漢模式、雙重鎖)

     有時,我們希望類的實例對象有且僅有一個,比如某個頁面,我們希望它如果出現,永遠只有一個,那麼此時你可能就需要用到單例模式了。(PS:本人親身經歷過手寫單例模式的面試,所以以下代碼必須能夠手撕!!!)

     單例模式:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。首先,怎麼讓類只有一個實例呢?肯定是構造函數需要做點“手腳”了,如果構造函數一如既往的是public屬性,那還是可以任意構造對象,則不滿足要求了。所以大神們想出來將構造函數私有化的方法,即把構造函數設置成私有屬性,並對外提供一個訪問的接口。

     單例模式的實現有兩種方式:懶漢模式和餓漢模式。懶漢模式:顧名思義,很“懶”,只有用到了才實例化對象並返回(調用了對外的接口才會實例化對象)。餓漢模式:不管調不調用對外接口,都已經實例化對象了。下面先實現基本的懶漢模式,代碼如下:

#include<iostream>

using namespace std;
/*單例模式:構造函數私有化,對外提供一個接口*/

//線程不安全的懶漢模式
class singleClass {
public:
	static singleClass* instance;//靜態成員變量,類內聲明,類外初始化
	static singleClass* getinstance()//對外的接口(方法),靜態成員函數調用靜態成員變量
	{
		if (instance == nullptr)
		{
			instance = new singleClass();
		}
		return instance;
	};

private:
	singleClass() {};//構造函數屬性設置爲私有
};
singleClass* singleClass::instance=nullptr;//初始化靜態變量

int main()
{
    //懶漢模式
	singleClass* singlep1=singleClass::getinstance();//通過類域獲取接口
	singleClass* singlep2 = singleClass::getinstance();

    cout << singlep1 << endl;
	cout << singlep2 << endl;

    system("pause");
	return 0;
}

由於沒有了對象,所以將instance設置爲static屬性,讓其能通過類名來訪問獲取。但是在多線程環境下,這種實現方式是不安全的,原因在於在判斷instance是否爲空時,可能存在多個線程同時進入if中,此時可能會實例化多個對象。於是,我瞭解決這個問題,出現了二重鎖的懶漢模式,實現代碼如下:

#include<iostream>
#include<mutex>

using namespace std;
/*單例模式:構造函數私有化,對外提供一個接口*/

//線程安全的單例模式
class lhsingleClass {
public:
	static lhsingleClass* instance;
	static mutex i_mutex;//鎖
	static lhsingleClass* getinstance()
	{//雙重鎖模式
		if (instance == nullptr)
		{//先判斷是否爲空,如果爲空則進入,不爲空說明已經存在實例,直接返回
            //進入後加鎖
			i_mutex.lock();
			if (instance == nullptr)
			{//再判斷一次,確保不會因爲加鎖期間多個線程同時進入
				instance = new lhsingleClass();
			}
			i_mutex.unlock();//解鎖
		}
		return instance;
	}
private:
	lhsingleClass(){}
};
lhsingleClass* lhsingleClass::instance=nullptr;
mutex lhsingleClass::i_mutex;//類外初始化


int main()
{
	lhsingleClass* lhsinglep5 = lhsingleClass::getinstance();
	lhsingleClass* lhsinglep6 = lhsingleClass::getinstance();

	cout << lhsinglep5 << endl;
	cout << lhsinglep6 << endl;
	system("pause");
	return 0;
}

以上就是單例模式的懶漢實現方式,下面介紹餓漢實現方式:

#include<iostream>

using namespace std;
/*單例模式:構造函數私有化,對外提供一個接口*/

//餓漢模式:不管用不用得到,都構造出來。本身就是線程安全的
class ehsingleClass {
public:
	static ehsingleClass* instance;//靜態成員變量必須類外初始化,只有一個
	static ehsingleClass* getinstance()
	{
		return instance;
	}

private:
	ehsingleClass() {}
};
ehsingleClass* ehsingleClass::instance = new ehsingleClass();
//類外定義,main開始執行前,該對象就存在了

int main()
{
	//餓漢模式
	ehsingleClass* ehsinglep3 = ehsingleClass::getinstance();
	ehsingleClass* ehsinglep4 = ehsingleClass::getinstance();
	//ehsingleClass* ehsinglep5 = ehsingleClass::get();//非靜態成員方法必須通過對象調用,不能通過類域訪問

	cout << ehsinglep3 << endl;
	cout << ehsinglep4 << endl;

	system("pause");
	return 0;
}

    餓漢式即一種靜態初始化的方式,它是類一加載就實例化對象,所以要提前佔用系統資源。而懶漢式又面臨着多線程不安全的問題,需要加二重鎖才能保證安全,因此具體使用哪種模式,需要根據實際需求和場景來定。

    以上就是單例模式的實現方式啦,本人也是剛學習總結,如有錯誤,歡迎指正交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章