有時,我們希望類的實例對象有且僅有一個,比如某個頁面,我們希望它如果出現,永遠只有一個,那麼此時你可能就需要用到單例模式了。(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;
}
餓漢式即一種靜態初始化的方式,它是類一加載就實例化對象,所以要提前佔用系統資源。而懶漢式又面臨着多線程不安全的問題,需要加二重鎖才能保證安全,因此具體使用哪種模式,需要根據實際需求和場景來定。
以上就是單例模式的實現方式啦,本人也是剛學習總結,如有錯誤,歡迎指正交流。