單例模式
?個類僅有?個實例,并提供一個訪問它的全局訪問點,該實例被所有程序模塊共享。
那么,我們必須保證:該類不能被復(fù)制;也不能被公開的創(chuàng)造。
對于 C++ 來說,它的構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)和賦值函數(shù)都不能被公開調(diào)用。
單例模式又分為 懶漢模式 和 餓漢模式 ,它們之間各有好處:
-
懶漢模式的實例在第一次被引用時才進行初始化,支持延遲加載,資源利用效率更高;但是當(dāng)資源訪問頻繁時,資源同步問題(加鎖、解鎖)會限制并發(fā)性能,也就是不支持高并發(fā)。
-
餓漢模式提前初始化實例,啟動時間較長;但是可以避免資源同步(加鎖、解鎖)帶來的性能消耗,后續(xù)的響應(yīng)時間更好。
餓漢模式
在加載類時,對象實例就被創(chuàng)建并初始化,在程序結(jié)束時自動銷毀,因此,它是線程安全的。
//.h文件
class Singleton {
public:
static Singleton* GetInstance(){
return _Instance;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator = (const Singleton&) = delete;
private:
static Singleton* _Instance;
};
// .CPP文件
Singleton* Singleton::_Instance = nullptr; // 類外初始化,必須寫
懶漢模式
在 C++11 標(biāo)準(zhǔn)中,靜態(tài)局部變量 的初始化是線程安全的,因此,可以用以下方式來編寫“懶漢”模式:
class Singleton {
public:
static Singleton& GetInstance() {
static Singleton instance;
return instance;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator = (const Singleton&) = delete;
};
更加傳統(tǒng)的寫法如下,使用 雙重檢查鎖定 來確保線程安全,通過靜態(tài)成員函數(shù) Destructor
來解決內(nèi)存泄漏:
//代碼實例(線程安全)
emplate<typename T>
class Singleton {
public:
static T* getInstance() {
if (_instance == nullptr) {
lock_guard<mutex> lock(_mutex);
if (_instance == nullptr) { // 雙重檢查鎖定, 確保線程安全
_instance = new T();
atexit(Destructor); // 在程序退出的時候釋放資源
}
}
return _instance;
}
private:
// 防止外界構(gòu)造/拷貝/刪除對象
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator = (const Singleton&) = delete;
static void Destructor() {
if (_instance != nullptr) {
delete _instance;
_instance = nullptr;
}
}
// 靜態(tài)指針
static T* _instance;
// 互斥鎖
static mutex _mutex;
};
// 初始化靜態(tài)指針
template<typename T>
T* Singleton<T>::_instance = nullptr;
// 初始化互斥鎖
template<typename T>
mutex Singleton<T>::_mutex;
但是,這種寫法可能出現(xiàn)以下問題:
由于 new 操作分為三步:分配內(nèi)存、返回指針、調(diào)用構(gòu)造函數(shù);如果一個線程在分配內(nèi)存后返回了指針,但還沒有構(gòu)造對象,另一個線程就嘗試訪問該對象的情況,就可能會出現(xiàn)未定義行為或錯誤。
因此,還是推薦使用“靜態(tài)局部變量”的寫法。
單例的應(yīng)用
需要強調(diào)的是,單例只是一種組織全局變量和靜態(tài)函數(shù)的方式 。
當(dāng)我們想要擁有應(yīng)用于某種全局?jǐn)?shù)據(jù)集的功能,并且我們想要重復(fù)使用時,單例是非常有用的。比如以下場景:
-
配置管理;
-
日志記錄;
-
消息隊列;
-
線程池、連接池、內(nèi)存池、對象池。
但是,單例模式是存在弊端的:
- 單例模式會隱藏類之間的依賴關(guān)系。
由于單例類不需要顯示地創(chuàng)建,也不需要依賴參數(shù)傳遞,在函數(shù)中直接調(diào)用就好,所以在閱讀代碼時,需要仔細(xì)閱讀才能清楚哪些類依賴了單例類。
- 單例模式的拓展性較差。
單例類只能創(chuàng)建一個實例,如果哪天需要在代碼中創(chuàng)建多個實例,則需要對代碼進行較大的改動。文章來源:http://www.zghlxwxcb.cn/news/detail-744442.html
以數(shù)據(jù)庫連接池為例,假設(shè)一開始我們將其設(shè)計為一個單例類,而后我們發(fā)現(xiàn),有些 SQL 語句的執(zhí)行效率低下,長時間占用連接資源,因此我們希望再創(chuàng)建一個連接池實例,讓它專門處理運行速度較慢的 SQL 語句,而此時,單例模式就對代碼的拓展性產(chǎn)生了影響。文章來源地址http://www.zghlxwxcb.cn/news/detail-744442.html
到了這里,關(guān)于【設(shè)計模式】C++單例模式詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!