單例模式的作用就是保證在整個應用程序的生命週期中,任何一個時刻,單例類的實例都只存在一個(當然也可以不存在)。
保證一個類只有一個實例,並且提供一個訪問該實例的全局訪問點。
實現單例模式的思路是:
- 一個類只有一個實例對象。在C++中一般是將構造函數、拷貝構造函數以及賦值操作符函數聲明爲private級別,從而阻止用戶實例化一個類。那麼,如何才能獲得該類的對象呢?需要類提供一個 public static的方法,通過該方法獲得這個類唯一的一個實例化對象。
- 當我們調用這個方法時,如果類持有的引用不爲空就返回這個引用,如果類保持的引用爲空就創建該類的實例,並將實例的引用賦予該類的靜態私有變量;
- 同時我們還將該類的構造函數定義爲私有方法,這樣其他處的代碼就無法通過調用該類的構造函數來實例化該類的對象,只有通過該類提供的靜態方法來得到該類的唯一實例。
單例模式根據實例化對象時機的不同分爲兩種:一種是餓漢式單例,一種是懶漢式單例。餓漢式單例在單例類被加載時候,就實例化一個對象交給自己的引用;而懶漢式在調用取得實例方法的時候纔會實例化對象。
餓漢模式
餓漢式(線程安全):類加載的時候對象就已經存在,在類創建的同時就已經創建好一個靜態的對象供系統使用,不管後面用不用這個類。(靜態初始化)
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton* GetInstance();
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* singleton;
};
Singleton* Singleton::singleton = new Singleton();
Singleton* Singleton::GetInstance()
{
return singleton;
}
int main()
{
Singleton* ct1 = Singleton::GetInstance();
Singleton* ct2 = Singleton::GetInstance();
if(ct1 == ct2)
cout<<"兩個對象是相同的實例"<<endl;
delete ct1;
return 0;
}
餓漢式是單例實現最簡單的方式,因此它的優點也是實現簡單,同樣缺點也非常明顯,做不到延遲加載。當單例類調用不是特別頻繁且存在大量資源佔用時,使用餓漢模式會導致單例類在程序初始時就被實例化,浪費系統資源。
懶漢模式
懶漢式(延遲初始化,線程不安全):該對象的實例只有在被需要的時候才進行創建,而不是一開始聲明的時候就創建。
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton* GetInstance();
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* singleton;
};
Singleton* Singleton::singleton = nullptr;
Singleton* Singleton::GetInstance()
{
if (singleton == nullptr)
singleton = new Singleton();
return singleton;
}
int main()
{
Singleton* ct1 = Singleton::GetInstance();
Singleton* ct2 = Singleton::GetInstance();
if(ct1 == ct2)
cout<<"兩個對象是相同的實例"<<endl;
delete ct1;
return 0;
}
優點:
- 避免了餓漢式的那種在沒有用到的情況下就創建事例,資源利用率高,不執行 GetInstance()方就不會被創建實例,可以執行該類的其他靜態方法。
缺點:
- 懶漢式在單個線程中沒有問題,然而這種寫法並不是線程安全的,因爲如果兩個線程同時到達 singleton == nullptr 的判斷時,就會各自創建一個實例,這樣就得到了兩個實例,這就違反了單例模式的規約。
懶漢式加鎖,雙重鎖的形式 (線程安全):
#include <iostream>
#include <Windows.h>
using namespace std;
class Singleton
{
private:
static Singleton* instance;
//臨界區,防止多線程產生多個實例
static CRITICAL_SECTION m_Sec;
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static CRITICAL_SECTION* getLock()
{
return &m_Sec;
}
static Singleton* GetInstance()
{
//雙重鎖定
if (instance == nullptr)
{
EnterCriticalSection(&m_Sec); //進入臨界區
if (instance == nullptr)//防止兩個線程進入臨界區,第一個進程創建完,第二個進程拿鎖又創建一個實例
instance = new Singleton();
LeaveCriticalSection(&m_Sec); //離開臨界區
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
CRITICAL_SECTION Singleton::m_Sec = CRITICAL_SECTION();
int main()
{
//初始化臨界區
InitializeCriticalSection(Singleton::getLock());
Singleton* singleton1 = Singleton::GetInstance();
Singleton* singleton2 = Singleton::GetInstance();
//刪除臨界區
DeleteCriticalSection(Singleton::getLock());
if (singleton1 == singleton2)
{
std::cout << "兩個對象是相同的實例。" << std::endl;
}
delete singleton1;
delete singleton2;
system("pause");
return 0;
}