C++設計模式之Singleton(單件/單例)模式

單例模式,顧名思義,此模式下的對象實例永遠只有一個.很多初學者感覺,這個模式的用處不大.但實際上,這個模式應該算是應用得最廣泛的模式之一了.

很多情況下,我們需要考慮單例的實現方式.如全局內存池;全局資源管理器;某個全局的工具類工廠...在UI開發上,同一時間點上僅會出現一個實例的對話框,也可以使用單例實現.

單例的類圖很簡單:

它的目的就是,給程序提供一個全局唯一的訪問點,用於訪問某些資源;固定的算法或唯一的對象等.

單例模式的構建方式主要有兩種:餓漢方式與懶漢方式.也就是靜態初始化的單例與運行時根據需要初始化的單例.

  • 餓漢方式

  

  • 懶漢方式

當我們在實際應用中,很可能需要考慮線程安全問題.餓漢方式由於實現採用類成員靜態初始化,始終是在主線程的主函數開始之前,以單線程方式進行的.所以當程序開啓多個線程開始同步訪問此單例類的時候,它總能返回唯一的單例.

然而懶漢方式則都是非線程安全的.就算是使用函數內部靜態初始化的懶漢單例,由於目前的C++標準並沒有規定編譯器需要解決static的線程安全性,因此它也不是線程安全的.

也就是說,一個static聲明的類變量,在多線程同時第一次訪問時將有可能被構造多次(普通類型的變量,如bool,int的static是線程安全的.在C++0x中規定了static必須由編譯器解決線程安全問題,因此支持C++0x的編譯器編譯的static類變量應該也不會有線程安全問題).

因此,我們可能需要這樣來寫線程安全的懶漢單例(注意,我這裏使用的鎖僅是語義上的"鎖",在實際應用中需要使用對應平臺提供的鎖來完成相應操作):

  • 線程安全的懶漢方式

在實際項目中,很多時候我們會用泛型的思想做單例模式,例如如下代碼:

  • 利用模板實現較爲通用的單例

這是一個利用模板實現的餓漢單例,並且是線程安全的.其他方式實現的單例模式也可以類似推出對應的模板實現.

上面說了那麼多,可以看出來,最方便的實現方式,應該是使用類成員靜態初始化實現的餓漢方式單例了.但是這種單例模式有個最大的硬傷,就是在C++裏默認的靜態成員初始化順序是不確定的.當一個程序中有多個單例,並且單例之間有相互的依賴時,就很可能出現因爲構造順序的不一致,導致當訪問某些單例時出現返回的對象尚未初始化的問題.

解決的方法有很多種,在這裏我一般是確定一個會被其他單例類依賴的單例,將它採用懶漢模式實現,而其他的單例則使用餓漢模式.此時的懶漢單例也不需要加鎖,因爲它必定會被其他的餓漢在構造的時候調用一次.


下面給出完整的示例代碼:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章