文章目錄
- 單例模式
- 創建單例類
- 餓漢式or懶漢式
- 現代C++單例模式的主流實現方式——Meyer's Singleton
- 使用 CRTP 模板化單例類
單例模式
單例模式是指程序中只需要一個實例化對象,在全局作用域或整個代碼架構中,此對象只被實例化一次,就可以達到在整個程序生命周期中被使用的目的。假如程序中設計了單例模式類,但是在程序設計中實例化了多個對象,那么這些對象也只占用同一塊地址空間,在代碼中可以通過"%p"
輸出的內存地址看出,這些對象是唯一的實例。
創建單例類
如何保證類的實例有且僅有一個?
涉及一個類多對象操作的函數有以下幾個:
- 構造函數:創建一個新的對象
- 拷貝構造函數:根據已有對象拷貝出一個新的對象
- 拷貝賦值操作符重載函數:兩個對象之間的賦值
為了把一個類可以實例化多個對象的路堵死,可以做如下處理:
- 構造函數私有化,在類內部只調用一次,這個是可控的。
- 由于使用者在類外部不能使用構造函數,所以在類內部創建的這個唯一的對象必須是靜態的,這樣就可以通過類名來訪問了,為了不破壞類的封裝,我們都會把這個靜態對象的訪問權限設置為私有的。
在類中只有它的靜態成員函數才能訪問其靜態成員變量,所以可以給這個單例類提供一個靜態函數用于得到這個靜態的單例對象。
- 由于使用者在類外部不能使用構造函數,所以在類內部創建的這個唯一的對象必須是靜態的,這樣就可以通過類名來訪問了,為了不破壞類的封裝,我們都會把這個靜態對象的訪問權限設置為私有的。
- 拷貝構造函數私有化或者禁用(使用 = delete)
- 拷貝賦值操作符重載函數私有化或者禁用(從單例的語義上講這個函數已經毫無意義,所以在類中不再提供這樣一個函數,故將它也一并處理一下。)
// 定義一個單例模式的類
class Singleton
{
public:Singleton(const Singleton& obj) = delete;Singleton& operator=(const Singleton& obj) = delete;static Singleton* GetInstance(){return instance;}
private:Singleton() = default;static Singleton* instance;
};//靜態成員變量只能在類外部初始化
Singleton* Singleton::instance = new Singleton();
餓漢式or懶漢式
餓漢模式 就是在類加載的時候立刻進行實例化,這樣就得到了一個唯一的可用對象。
適用于內存大的場景,多線程調用沒有線程安全問題。
懶漢模式 是在類加載的時候不去創建這個唯一的實例,而是在需要使用的時候再進行實例化。
適用于內存緊張的場景,多線程調用有線程安全問題。
參考文章:https://subingwen.cn/design-patterns/singleton/
現代C++單例模式的主流實現方式——Meyer’s Singleton
Meyer’s Singleton是典型的懶漢模式:
- 線程安全(
C++11
起):局部靜態變量的初始化是線程安全的(編譯器會自動加鎖)。 - 延遲初始化(Lazy Initialization):只有當
GetInstance()
第一次被調用時才會構造對象。 - 自動資源管理:程序結束時,靜態變量會自動析構(除非你使用了動態分配)。
- 高效無拷貝:返回的是引用,不會產生拷貝,效率高。
class Singleton
{
public:Singleton(const Singleton& obj) = delete;Singleton& operator=(const Singleton& obj) = delete;static Singleton& GetInstance(){static Singleton instance;return instance;}
private:Singleton() = default;~Singleton() = default;
};
使用 CRTP 模板化單例類
CRTP(Curiously Recurring Template Pattern)奇異遞歸模板模式 是C++中的一個編程技巧,它允許基類使用派生類的類型信息。這種模式在靜態多態、計數器等場景中有應用,例如在LLVM項目中被廣泛使用。通過CRTP,可以避免虛函數調用的開銷,提供更高效和靈活的代碼設計。
CRTP的特性表現為:
- 基類是一個模板類
- 派生類繼承該基類時,將派生類自身作為模板參數傳遞給基類
實現一個 模板化的單例類,并使用 CRTP
來讓任意類輕松成為單例,可以這樣寫:
template<typename T>
class Singleton
{
protected:Singleton() = default;~Singleton() = default;public:Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;Singleton(Singleton&&) = delete;Singleton& operator=(Singleton&&) = delete;static T& GetInstance(){static T instance;return instance;}
};template<typename T> T& g = Singleton<T>::GetInstance();
使用方式
class MyClass : public Singleton<MyClass>
{friend class Singleton<MyClass>; // 保證 GetInstance 可以訪問構造函數
private:MyClass() { /* 構造邏輯 */ }
public:void DoSomething() { /* ... */ }
};// 使用
MyClass::GetInstance().DoSomething();
// 或
g<MyClass>.DoSomething();