1 什么是單例模式
單例模式 是一種創建型設計模式,確保一個類在整個程序生命周期中只有一個實例,并提供一個全局訪問點。
核心要求:
- 類不能被外部隨意創建(禁止 public 構造函數或限制實例數量)。
- 不能被復制或移動。
- 提供一個全局訪問接口(如?
getInstance()
)。- 實例的生命周期通常伴隨整個程序。
2 模版單例的實現
2.1 單例模版GSingleton<T>
使用場景:項目中需要大量單例,且希望統一管理時,使用單例模版會提高代碼復用。單例模版類本身不能直接使用,而是作為基類被繼承。需要實現單例的類只需要繼承GSingleton即可。
注意:模版基類GSingleton的構造函數和析構函數必須是protected修飾,不能用private,因為派生類在構造時或者析構時可以調用。而派生類T的構造函數和析構函數可以由private修飾。
template<typename T>
class GSingleton
{
protected:GSingleton() = default;~GSingleton() = default; // 非虛,除非你真的需要// 禁止拷貝和移動GSingleton(const GSingleton&) = delete;GSingleton& operator=(const GSingleton&) = delete;GSingleton(GSingleton&&) = delete;GSingleton& operator=(GSingleton&&) = delete;public:static T& getInstance() {static T instance;return instance;}
};
- 模版類詳細介紹
- protect:修飾的方法或者成員變量只能被本類、派生類以及友元訪問。
- Gsingleton<T>是基類,必須是protected,這樣派生類在構造時才可以調用父類的構造函數。
- GSingleton = default:表示顯式要求編譯器生成一個默認構造函數(無參構造函數)
- ~GSingleton() = default:表示讓編譯器生成默認析構函數。
- GSingleton(const GSingleton&) = delete:這是對拷貝構造函數的聲明,禁止拷貝。
const GSingleton&:表示對另一個GSingleton對象的常量引用。
delete表示這個函數被顯示刪除,不能使用。Access a; Access b(a); // ? 錯誤!調用拷貝構造函數 Access c = a; // ? 錯誤!也是拷貝構造(賦值語法,實際是構造)
- GSingleton& operator=(const GSingleton&) = delete:這是對拷貝賦值運算符的聲明,禁止拷貝。
operator=:表示賦值操作符的重載。Access a; Access b; b = a; // ? 錯誤!調用拷貝賦值
- GSingleton(GSingleton&&) = delete:這是移動構造函數的聲明,禁止移動構造。
GSingleton&&:是一個右值引用,表示臨時對象或者可移動的對象。
移動不是拷貝,而是資源轉移,將一個臨時對象所擁有的資源轉移給另一個對象,而原對象不再擁有該資源。
移動避免了不必要的內存分配和數據賦值,因此非常高效。Access a; Access b(std::move(a)); // ? 錯誤!試圖移動構造(即使 NRVO 優化,語義上也不允許)
- GSingleton& operator=(GSingleton&&) = delete:這是移動賦值運算符,禁止將一個臨時對象移動賦值給已有對象。
Access a; Access b; b = std::move(a); // ? 錯誤!禁止移動賦值
??總結
代碼 | 禁止的操作 |
---|---|
Access(const Access&) = delete; | Access b(a); ?或?Access b = a; |
Access& operator=(const Access&) = delete; | b = a; |
Access(Access&&) = delete; | Access b(std::move(a)); |
Access& operator=(Access&&) = delete; | b = std::move(a); |
2.2 使用單例模版定義派生類
class GLoggerImpl : public GSingleton<GLoggerImpl>
{
private:GLoggerImpl();friend class GSingleton<GLoggerImpl>;
public:~GLoggerImpl();void WriteLog(int logLevel, const char* file, int line, const char* function, const char* format, va_list args);
}
注意:在聲明GloggerImpl的類時使用了friend class GSingleton<GLoggerImpl>,表示聲明讓模版類成為友元。
原因:在調用GLoggerImpl::getInstance()時,
// template<typename T> class GSingleton
static T& getInstance() {static T instance; // ← 這里要構造 T,即 GLoggerImplreturn instance;
}
static T instance會調用GLoggerImpl的構造函數,由于GLoggerImpl()是private或者protected,而GSingleton<GLoggerImpl>模版類不是GLoggerImpl類的成員變量,默認無法訪問GLoggerImpl的構造函數,因此要聲明friend來顯示授權。
友元:是C++的一個關鍵字,它允許一個類、函數或者模版訪問另一個類的private和protected成員,即使它不是那個類的成員函數或者派生類。
- friend可以修飾函數,稱為友元函數,可以在一個類中聲明友元函數,這樣該函數可以訪問該類的私有成員變量或者成員函數。
- friend可以修飾類,成為友元類,可以訪問所在類中的private或者protect的成員,友元關系是單向的。
- friend可以修飾一個模版類或者模版函數,成為友元模版。例如GloggerImpl中friend修飾的模版。
2.3 單例類的使用
由于GLoggerImpl繼承了GSingleton的模版基類,因此繼承了父類靜態的getInstance()方法。所以通過GLoggerImpl::getInstance()可以獲取該類唯一的單例對象。
void LogD(const char* file, int line, const char* function, const char* format, ...) {va_list args;va_start(args, format);GLoggerImpl::getInstance()->WriteLog(LEVEL_DEBUG, file, line, function, format, args);va_end(args);
}
3 單例類的實現
使用場景:項目里只需要少數單例,且追求高可讀性和維護性,推薦單例類的實現。
該實現是線程安全的,使用靜態局部變量方式實現。
class Singleton {
public:static Singleton& getInstance() {static Singleton instance; // C++11 起,線程安全return instance;}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;Singleton(Singleton&&) = delete;Singleton& operator=(Singleton&&) = delete;private:Singleton() = default;~Singleton() = default;
};
使用:Singleton s = Singleton::getInstance()就獲取該類的唯一單例。