在講單例模式之前,先說一下設計模式
設計模式
一些人將自己的編程經驗,通過一些常見的問題或者常見的場景,給出一種解決方案或者一種套路,讓後續的程序員在遇到相似問題時可以快速設計自己的代碼,即一套被反覆使用、多數人知曉的、經過分類的、代碼設計經驗的總結
常見設計模式
創建性模式:用於構建對象,將實現從系統當中分離處理
單例模式
結構性模式:用於許多不同對象之間形成大型的對象結構
適配器模式
行爲型模式
觀察者模式
單例模式
一個類只能創建一個對象,即單例模式,該模式可以保證系統中該類只有一個實例,並提供一個訪問它的全局訪問點,該實例被所有程序模塊共享。比如在某個服務器程序中,該服務器的配置信息存放在一個文件中,這些配置數據由一個單例對象統一讀取,然後服務進程中的其他對象再通過這個單例對象獲取這些配置信息,這種方式簡化了在複雜環境下的配置管理
特點
提供了一個唯一類的實例,具有全局變量的特點,在任何位置都可以通過單例類提供的獲取類實例的方法來獲取唯一實例
使用場景
數據池:用來緩存大量數據的數據結構,需要在不同的線程當中進行讀取或者寫入
內存池:爲了程序的運行效率,將申請的內存自行管理,即當使用完成的時候不歸還操作系統,自行管理
基礎要點
全局只有一個實例
static 特性:禁止自己的構造、拷貝構造、複製拷貝
線程安全:用戶通過接口獲取實例,使用 static 修飾類的成員函數
餓漢模式
程序在初始化的時候進行實例化,資源在程序初始化的時候全部加載,不牽扯到線程安全
優缺點:運行流暢,但初始化耗時較長
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
// 構造函數私有
Singleton(){};
// C++98 防拷貝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
// C++11
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static Singleton m_instance;
};
Singleton Singleton::m_instance; // 在程序入口之前就完成單例對象的初始化
懶漢模式
資源在使用時才進行加載,即對象在使用的時候才實例化,存在線程不安全的風險
優缺點:運行沒有那麼流暢,存在線程安全問題,初始化較塊
class Singleton
{
public:
static Singleton* GetInstance()
{
// 使用雙檢查的方式加鎖,才能保證效率和線程安全
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;
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;
}