一、概述
? ? ? ? 單例模式也稱單態(tài)模式,是一種創(chuàng)建型模式,用于創(chuàng)建只能產(chǎn)生一個對象實例的類。例如,項目中只存在一個聲音管理系統(tǒng)、一個配置系統(tǒng)、一個文件管理系統(tǒng)、一個日志系統(tǒng)等,甚至如果吧整個Windows操作系統(tǒng)看成一個項目,那么其中只存在一個任務管理器窗口等。引入單例模式的實現(xiàn)意圖:保證一個類僅有一個實例存在,同時提供能對該實例訪問的全局方法。
二、單例模式分類
1、懶漢模式
1)代碼示例
class CSingletonImpl
{
public:
?? ?static CSingletonImpl* GetInstance()
?? ?{
?? ??? ?if (m_pInstance == nullptr)
?? ??? ?{
?? ??? ??? ?m_pInstance = new CSingletonImpl;
?? ??? ?}
?? ??? ?return m_pInstance;
?? ?}
private:
?? ?CSingletonImpl(){};
?? ?~CSingletonImpl(){};
?? ?CSingletonImpl(const CSingletonImpl& the);
?? ?CSingletonImpl& operator=(const CSingletonImpl& other);
private:
?? ?static CSingletonImpl* m_pInstance;
};
CSingletonImpl*CSingletonImpl::m_pInstance = nullptr;
2)說明
單例模式為了防止多對象問題,將構(gòu)造函數(shù),析構(gòu)函數(shù),拷貝構(gòu)造函數(shù),賦值運算符函數(shù)設置為私有,同時設置公有唯一接口方法來創(chuàng)建對象,同時定義類靜態(tài)指針。這是通用方法,那么會有什么問題呢?如果在單一線程中使用則沒什么問題,但是在多線程中使用則可能導致問題,如果多個線程可能會因為操作系統(tǒng)時間片調(diào)度問題切換造成多對象產(chǎn)生,那么解決這個問題的方案就是對GetInstance()成員函數(shù)枷鎖。
示例代碼:
加入私有成員變量:static std::mutex m_mutex;
static CSingletonImpl* GetInstance()
{
?m_mutex.lock();
?if (m_pInstance == nullptr)
?{
? m_pInstance = new CSingletonImpl;
?}
?m_mutex.unlock();
?return m_pInstance;
}
加入以上代碼沒有問題了嗎?呵呵,還不行,雖然對接口函數(shù)加鎖,從代碼邏輯上沒有問題,實現(xiàn)了線程安全,但是從執(zhí)行效率上來說,是有大問題的。當程序運行中GetInstance()可能會被多個線程頻繁調(diào)用,每次調(diào)用都會經(jīng)歷加鎖解鎖的過程,這樣的話會嚴重影響程序執(zhí)行效率,而且加鎖機制僅僅對第一次創(chuàng)建對象有意義,對象一旦創(chuàng)建則變成只讀對象,在多線程中,對只讀對象的訪問加鎖不僅代價大,而且無意義。那么如何解決這個問題呢?那就是雙重鎖定機制,基于這種機制函數(shù)實現(xiàn)代碼:
?? ?static CSingletonImpl* GetInstance()
?? ?{
?? ??? ?if (m_pInstance == nullptr)
?? ??? ?{
?? ??? ??? ?std::lock_guard<std::mutex> siguard(si_mutex);
?? ??? ??? ?if (m_pInstance == nullptr)
?? ??? ??? ?{
?? ??? ??? ??? ?m_pInstance = new CSingletonImpl;
?? ??? ??? ?}
?? ??? ?}
?? ??? ?return m_pInstance;
?? ?}
上述雙重鎖定機制看起來比較完美,但實際上存在潛在的問題,內(nèi)存訪問重新排序?qū)е码p重鎖定失效的問題,比較推薦的方法時C++11新標準的一些特性,示例代碼如下:
#include <mutex>
#include <atomic>
//通過原子變量解決雙重鎖定底層問題(load,store)
class CSingletonImpl
{
public:
?? ?static CSingletonImpl* GetInstance()
?? ?{
?? ??? ?CSingletonImpl* task = m_taskQ.load(std::memory_order_relaxed);?
?? ??? ?std::atomic_thread_fence(std::memory_order_acquire);
?? ??? ?if (task == nullptr)
?? ??? ?{
?? ??? ??? ?std::lock_guard<std::m_mutex> lock(m_mutex);
?? ??? ??? ?task = m_taskQ.load(std::memory_order_relaxed);?
?? ??? ??? ?if (task == nullptr)
?? ??? ??? ?{
?? ??? ??? ??? ?task = new CSingletonImpl;
?? ??? ??? ??? ?std::atomic_thread_fence(std::memory_order_release);
?? ??? ??? ??? ?m_taskQ.store(task, std::memory_order_relaxed);
?? ??? ??? ?}
?? ??? ?}
?? ??? ?return task;
?? ?}
private:
?? ?CSingletonImpl(){};
?? ?~CSingletonImpl(){};
?? ?CSingletonImpl(const CSingletonImpl& the);
?? ?CSingletonImpl& operator=(const CSingletonImpl& other);
private:
?? ?static std::mutex m_mutex;
?? ?static std::atomic<CSingletonImpl*> m_taskQ;
};
std::mutex CSingletonImpl::m_mutex;
std::atomic<CSingletonImpl*> CSingletonImpl::m_taskQ;
2、餓漢模式
1)示例代碼
class CSingletonImpl
{
public:
?? ?static CSingletonImpl* GetInstance()
?? ?{
?? ??? ?return m_pInstance;
?? ?}
private:
?? ?CSingletonImpl(){};
?? ?~CSingletonImpl(){};
?? ?CSingletonImpl(const CSingletonImpl& the);
?? ?CSingletonImpl& operator=(const CSingletonImpl& other);
private:
?? ?static CSingletonImpl* m_pInstance;
};
CSingletonImpl*CSingletonImpl::m_pInstance = new CSingletonImpl();
2)說明
此類模式可稱為餓漢式--------程序一執(zhí)行不管是否調(diào)用了GetInstance()成員函數(shù),這個單例類對象就已經(jīng)被創(chuàng)建了。在餓漢式單例類代碼的實現(xiàn)必須要注意,如果一個項目中有多個.cpp源文件,而且這些源文件中包含對全局變量的初始化代碼,例如某個.cpp中可能存在如下代碼:
int g_test =?CSingletonImpl::GetInstance()->m_i; //m_i是int類型變量
那么這樣的代碼是不安全的,因為多個源文件中全局變量的初始化順序是不確定的,很可能造成GetInstance()函數(shù)返回是nullptr,此時去訪問m_i成員變量肯定會導致程序執(zhí)行異常。所以,對餓漢式單例類對象的使用,應該在程序入口函數(shù)開始執(zhí)行后,例如main函數(shù)后。文章來源:http://www.zghlxwxcb.cn/news/detail-634096.html
注意:函數(shù)第一次執(zhí)行時被初始化的靜態(tài)變量與通過編譯器常量進行初始化的基本類型靜態(tài)變量這兩種情況,不要再單例類的析構(gòu)函數(shù)中引用其他單例類對象。文章來源地址http://www.zghlxwxcb.cn/news/detail-634096.html
到了這里,關于C++設計模式創(chuàng)建型之單例模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!