C++單例模式


1、單例模式:
單例模式:用來創建獨一無二的,只能夠有一個實例的對象。 單例模式的結構是設計模式中最簡單的,但是想要完全實現一個線程安全的單例模式還是有很多陷阱的。

2、應用場景:
共享數據或者共享訪問點;
創建一個對象需要消耗的資源過多,如訪問IO和數據庫等資源;
需要定義大量的靜態常量和靜態方法(工具類)

單例模式的應用場景:有一些對象其實只需要一個,比如:windows Task Manager (任務管理器)、windows 回收站、線程池、緩存、對話框、處理偏好設置和注冊表的對象、日志對象、充當打印機、顯卡等設備的驅動程序對象。這些對象只能夠擁有一個實例,如果創建出了多個實例,就會導致一些程序的問題。程序的行為異常,資源使用的過量,或者導致不一致的結果。常用來管理共享的資源,比如數據庫的連接或者線程池。

3、優缺點:
優點:一個實例,減少內存開銷;減少系統開銷;避免對一個資源的多重占用;設置全局訪問點,優化和貢獻資源。

缺點:沒有借口,擴展性差;不利於測試;與單一職責原則有沖突,單例模式把“要單例”和業務邏輯融合在一個類中。

4、實現
1、經典實現(線程不安全)

 1 class Singleton 
 2 { 
 3 public: 
 4 static Singleton* getInstance(); 
 5 protected: 
 6 Singleton(){} 
 7 private: 
 8 static Singleton *p; 
 9 };
10 
11 Singleton* Singleton::p = NULL; 
12 Singleton* Singleton::getInstance() 
13 { 
14 if (NULL == p) 
15 p = new Singleton(); 
16 return p; 
17 }
 1 1、當兩個線程運行至if(instance==NULL)時,可能產生線程安全問題。
 2 
 3 改進:在用到的時候在初始化,這樣程序效率比較高,但是有一個另外比較好的方法可以采用是提前初始化,將Instance設置為static之后直接初始化為Singleton對象,每次只需要執行返回操作即可。
 4 
 5 class Singleton {
 6 public:
 7     static Singleton* getInstance();
 8 
 9 protected:
10 
11     Singleton();
12 
13 private:
14     static Singleton *instance;
15 };
16 
17 //對單例直接進行初始化,而不再方法Instance中進行判斷。
18 
19 Singleton* Singleton::instance = new Singleton;
20 
21 Singleton* Singleton::getInstance()
22 
23 {
24 
25   return instance;
26 
27 }
28 這樣的話同樣會導致問題,就是如果單例本來資源比較多,但是不需要創建那么早,就會消耗資源~
29 
30 2、另外一種提升因為同步導致的性能變差的方法稱為“雙重檢驗加鎖”。方法如下:
31 
32 class Singleton {
33 public:
34     static Singleton* getInstance();
35 protected:
36     Singleton();
37 private:
38     static Singleton* instance;
39 }
40 
41 Singleton *Singleton::instance = NULL;
42 Singleton* Singleton::getInstance() {
43     //check 之前進行臨界區加鎖操作
44     //雙重檢驗加鎖
45     if(_instance == NULL ) {
46         lock();
47         if( instance == NULL) {
48             instance = new Singleton();
49         }
50         Unlock();
51     }
52     return _instance;
53 }
54 
55 思路是只有在第一次創建的時候進行加鎖,當instance不為空的時候就不需要進行加鎖的操作,這樣就可以提升性能~

2、懶漢模式與餓漢模式
懶漢:故名思義,不到萬不得已就不會去實例化類,也就是說在第一次用到類實例的時候才會去實例化,所以上邊的經典方法被歸為懶漢實現;

餓漢:餓了肯定要飢不擇食。所以在單例類定義的時候就進行實例化。
特點與選擇

區別:由於要進行線程同步,所以在訪問量比較大,或者可能訪問的線程比較多時,采用餓漢實現,可以實現更好的性能。這是以空間換時間。在訪問量較小時,采用懶漢實現。這是以時間換空間。

 1 1.加鎖實現線程安全的懶漢模式
 2 
 3 class Singleton 
 4 { 
 5   public: 
 6     static pthread_mutex_t mutex; 
 7     static Singleton* getInstance(); 
 8   protected: 
 9     Singleton() 
10     { 
11       pthread_mutex_init(&mutex); 
12     } 
13   private: 
14     static Singleton* p; 
15 }; 
16 
17 pthread_mutex_t Singleton::mutex; 
18 Singleton* Singleton::p = NULL; 
19 Singleton* Singleton::getInstance() 
20 { 
21   if (NULL == p) 
22   { 
23     pthread_mutex_lock(&mutex); 
24     if (NULL == p) 
25       p = new Singleton(); 
26     pthread_mutex_unlock(&mutex); 
27   } 
28   return p; 
29 }
30 
31 2.內部靜態變量實現懶漢模式
32 
33 class Singleton 
34 { 
35   public: 
36   static pthread_mutex_t mutex; 
37   static Singleton* getInstance(); 
38   protected: 
39     Singleton() 
40     { 
41       pthread_mutex_init(&mutex); 
42     } 
43 }; 
44 
45 pthread_mutex_t Singleton::mutex; 
46 Singleton* Singleton::getInstance() 
47 { 
48   pthread_mutex_lock(&mutex); 
49   static singleton obj; 
50   pthread_mutex_unlock(&mutex); 
51   return &obj; 
52 }
 1 餓漢模式
 2 
 3 class Singleton 
 4 { 
 5   public: 
 6     static Singleton* getInstance(); 
 7   protected: 
 8     Singleton(){} 
 9   private: 
10     static Singleton* p; 
11 }; 
12 
13 Singleton* Singleton::p = new Singleton; 
14 Singleton* Singleton::getInstance() 
15 { 
16   return p; 
17 } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM