單例設計模式
設計模式(Design Pattern)是一套被反復使用、多數人知曉的、經過分類的、代碼設計經驗的總結,
為什么會產生設計模式這樣的東西呢?
使用設計模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性, 設計模式使代碼撰寫真正工程化;設計模式是軟體工程的基石脈絡,如同大廈的結構一樣,
其中最具代表性的就是單例設計模式
單例設計模式
一個類只能創建一個物件,即單例模式,該模式可以保證系統中該類只有一個實體,并提供一個訪問它的全域訪問點,該實體被所有程式模塊共享,比如在某個服務器程式中,該服務器的配置資訊存放在一個檔案中,這些配置資料由一個單例物件統一讀取,然后服務行程中的其他物件再通過這個單例物件獲取這些配置資訊,這種方式簡化了在復雜環境下的配置管理,
在linux下曾提到過單例設計模式:
實作:
- 餓漢模式:資源在程式初始化階段就完成加載(空間換時間)
- 餓漢方法實作:
- 采用靜態方法修飾資源,所有物件共用同一份資源,程式初始化階段完成資源加載且只被加載一次
- 建構式私有化 ,保證一個類只能實體化一個物件
template <typename T>
class Singleton {
static T data;
Singleton(){}//建構式私有化
public:
static T* GetInstance() {
return &data;
}
};
- 懶漢模式:資源在使用的時候再去加載(延遲加載)
- 定義物件指標,初始資源為空
- static修飾,共用一份資源
- volatite修飾 ,防止編譯器過度優化
- 加鎖保護 執行緒安全
- 二次檢測,防止鎖沖突
template <typename T>
class Singleton {
volatite static T* inst;//定義物件指標 初始為空
static std::mutex _mutex;
public:
static T* GetInstance() {
if(inst==NULL)//double check 二次檢測,避免不為空情況依然加鎖,造成鎖沖突
{
_mutex.lock();
if (inst == NULL) {
inst = new T();//呼叫時發現為空,進行獲取資源
}
_mutex.unlock();
}
return inst;
}
};
總結來說,單例設計主要分為兩種:
- 餓漢模式(提前回應)
- 懶漢模式(延遲加載)
-
餓漢模式
在C++11標準下,餓漢模式要求:程式啟動時就創建一個唯一的實體物件
要滿足此條件,提前創建物件,只有將其定義為全域變數或靜態變數,才會程式啟動前完成創建
// 餓漢模式
// 優點:簡單
// 缺點:可能會導致行程啟動慢,且如果有多個單例類物件實體啟動順序不確定,
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
// 建構式私有
Singleton(){};
// C++11防拷貝
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static Singleton m_instance;
};
Singleton Singleton::m_instance; // 在程式入口之前就完成單例物件的初始化
注:
1,建構式私有 外部無法創建物件
2,通過介面 指標獲取已創建物件
3,類內 定義靜態成員物件,類外初始化
4,定義靜態成員,靜態成員定義在類內,類內可以訪問私有成員函式完成構造,且靜態成員存盤在靜態資料區,與類不為同一塊存盤空間
-
懶漢模式
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Singleton
{
public:
static Singleton* GetInstance() {
// 注意這里一定要使用Double-Check的方式加鎖,才能保證效率和執行緒安全
if (nullptr == m_pInstance) {//第一次判斷防止多次加鎖導致鎖沖突
m_mtx.lock();
if (nullptr == m_pInstance) {//第二次判斷是否為空,為空則為第一次呼叫建構式創建,不為空,后續都不會呼叫建構式,滿足物件第一次創建且僅創建一次
m_pInstance = new Singleton();
}
m_mtx.unlock();
}
return m_pInstance;
}
// 實作一個內嵌垃圾回收類
class CGarbo {
public:
~CGarbo(){
if (Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
};
// 定義一個靜態成員變數,程式結束時,系統會自動呼叫它的解構式從而釋放單例物件
static CGarbo Garbo;
private:
// 建構式私有
Singleton(){};
// 防拷貝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
static Singleton* m_pInstance; // 單例物件指標 靜態成員變數,在呼叫建構式前先創建進行判斷
static mutex m_mtx; //互斥鎖
};
Singleton* Singleton::m_pInstance = nullptr;//靜態成員變數類外初始化,第一次初始化為nullptr
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;
void func(int n)
{
cout<< Singleton::GetInstance() << endl;
}
// 多執行緒環境下演示上面GetInstance()加鎖和不加鎖的區別,
int main()
{
thread t1(func, 10);
thread t2(func, 10);
t1.join();
t2.join();
cout << Singleton::GetInstance() << endl;
cout << Singleton::GetInstance() << endl;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286218.html
標籤:其他
上一篇:[MCTF] 2021校賽題解
