您好,登錄后才能下訂單哦!
一.概述:
因為在設計或開發中,肯定會有這么一種情況,一個類只能有一個對象被創建,如果有多個對象的話,可能會導致狀態的混亂和不一致。這種情況下,單例模式是最 恰當的解決辦法。有很多地方需要這樣的功能模塊,如系統的日志輸出,GUI應用必須是單鼠標,MODEM的聯接需要一條且只需要一條電話線,操作系統只能有一個窗口管理器,一臺PC連一個鍵盤。單例模式有很多種實現方式,各自的特性不相同,使用的情形也不相同。今天要實現的是常用的三種,分別是餓漢式、懶漢式和多線程式。
《設計模式》一書中的實現有三個要素,定義一個單例類,要使用類的私有靜態指針變量指向類的唯一實例(即在類中就生成一個對象),并用一個公有的靜態方法獲取該實例,并把構造函數定義為protected或private。
二.懶漢式實現單例模式:
懶漢式的特點是延遲加載,懶漢么,很懶,它只在要用到實例時才加載實例。
/**************************************** 2 > File Name:lanhan.cpp 3 > Author:xiaoxiaohui 4 > mail:1924224891@qq.com 5 > Created Time:2016年05月07日 星期六 15時01分25秒 6 ****************************************/ 7 8 #include<iostream> 9 using namespace std 10 11 class Singleton 12 { 13 private: 14 Singleton() 15 {} 16 static Singleton* _instace; //靜態的 私有的 17 public: 18 static Singleton* GetInstace() 19 { 20 if(_instance == NULL) 21 { 22 _instance = new Singleton(); 23 } 24 return _instance; //如果非空則new一個對象 后者返回原來的兩個對象(所以保證了只有一個對象生成) 25 } 26 27 } 28
上面的這一實現存在內存泄露問題,因為沒有釋放_instance指針,下面為懶漢式的改進版:
8 #include<iostream> 9 using namespace std 10 11 class Singleton 12 { 13 private: 14 Singleton() 15 {} 16 static Singleton* _instance; //靜態的 私有的 17 18 class del 19 { 20 public: 21 ~del() 22 { 23 if(Singleton::_instance != NULL) 24 { 25 delete Singleton::_instance; 26 Singleton::_instance = NULL; 27 } 28 } 29 } 30 static del d; //靜態變量會在程序結束時調用它的析構函數 31 public: 32 static Singleton* GetInstance() 33 { 34 if(_instance == NULL) 35 { 36 _instance = new Singleton(); 37 } 38 return _instance; //如果非空則new一個對象 后者返回原來的兩個對象(所以保證了只有一個對象生成) 39 } 40 41 }
該實現會在程序結束時調用靜態變量的析構函數,從而delete了唯一的Singleton對象。
使用這種方法釋放單例對象有以下特征:
1.在單例類內部定義專有的嵌套類。
2.在單例類內定義私有的專門用于釋放的靜態成員。
3.利用程序在結束時析構全局變量的特性,選擇最終的釋放時機。
但是現在還有問題,如果在多線程環境下,因為“if(_instance == NULL)”并不是原子的,會存在線程安全問題(如果一個線程剛剛判斷了指針為空,這時另一個線程的優先級更高或者其它原因,打斷了原來線程的執行,再次判斷指針也會為空,所以會出現兩個實例)下面為多線程環境下的懶漢式單例模式:
8 #include<iostream> 9 #include<stdlib.h> 10 #include<pthread.h> 11 12 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 13 using namespace std 14 15 class Singleton 16 { 17 private: 18 Singleton() 19 {} 20 static Singleton* _instance; //靜態的 私有的 21 22 class del 23 { 24 public: 25 ~del() 26 { 27 if(Singleton::_instance != NULL) 28 { 29 delete Singleton::_instance; 30 Singleton::_instance = NULL; 31 } 32 } 33 } 34 static del d; //靜態變量會在程序結束時調用它的析構函數 35 public: 36 static Singleton* GetInstance() 37 { 38 pthread_mutex_lock(&lock); 39 if(_instance == NULL) 40 { 41 _instance = new Singleton(); 42 } 43 pthread_mutex_unlock(&lock); 44 return _instance; //如果非空則new一個對象 后者返回原來的兩個對象(所以保證了只有一個對象生成) 45 } 46 47 } 48
但現在還有問題,當有大量的線程時,只會有一個線程進入互斥鎖,然后執行下面的代碼而其它線程只能等待,并且加鎖是一個繁重的過程,這樣會導致加很多次鎖,這樣就太不高效了。下面是高效版的多線程環境下的懶漢式單例模式:
8 #include<iostream> 9 #include<stdlib.h> 10 #include<pthread.h> 11 12 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 13 using namespace std 14 15 class Singleton 16 { 17 private: 18 Singleton() 19 {} 20 static Singleton* _instance; //靜態的 私有的 21 22 class del 23 { 24 public: 25 ~del() 26 { 27 if(Singleton::_instance != NULL) 28 { 29 delete Singleton::_instance; 30 Singleton::_instance = NULL; 31 } 32 } 33 } 34 static del d; //靜態變量會在程序結束時調用它的析構函數 35 public: 36 static Singleton* GetInstance() 37 { 38 if(_instance == NULL) 39 { 40 pthread_mutex_lock(&lock); 41 if(_instance == NULL) 42 { 43 _instance = new Singleton(); 44 } 45 pthread_mutex_unlock(&lock); 46 } 47 return _instance; //如果非空則new一個對象 后者返回原來的兩個對象(所以保證了只有一個對象生成) 48 } 49 50 }
這樣只有沒有Singleton實例時才會進入加鎖的代碼,而當有Singleton實例時不需要進入加鎖的代碼中,直接返回已存在的實例就行了。
三.餓漢式的單例模式:在一開始就創建實例,要用時直接返回即可。餓汗式的單例模式沒有線程安全問題,因為所以線程都只能訪問一個已存在的對象,無論線程怎么調度都不會有多個對象出現。因為對象是一個靜態變量(不是指針),會在程序結束時自動調用它的析構函數,所以不用考慮內存泄露問題。
餓汗式的特點:代碼簡單,不會出現內存泄露,是線程安全的。
1 /**************************************** 2 > File Name:erhan.cpp 3 > Author:xiaoxiaohui 4 > mail:1924224891@qq.com 5 > Created Time:2016年05月07日 星期六 16時10分56秒 6 ****************************************/ 7 8 #include<iostream> 9 using namespace std 10 11 12 class Singleton 13 { 14 private: 15 Singleton() 16 {} 17 static Singleton instance ; //靜態變量只會有一份數據存在 從而保證只有一個實例 18 public: 19 static Singleton& GetInstance() 20 { 21 return instance; 22 } 23 }
聲明一個局部的靜態變量,而靜態變量在全局范圍內只有一份數據,所以無論調用多少此GetInstance,返回的都是那一個實例。
但這個實現存在問題,Singleton singleton = Singleton :: GetInstance(),這么做就出現了一個類拷貝的問題,這就違背了單例的特性。產生這個問題原因在于:因為在這里沒有實現拷貝構造函數,編譯器會為類生成一個默認的拷貝構造函數,來支持類的拷貝。
解決方法:1.自己再定義一個拷貝構造函數和operator=,這個拷貝構造函數和operator=什么都不做。
2.返回一個Singleton指針。
下面為方法2的代碼:
8 #include<iostream> 9 using namespace std 10 11 12 class Singleton 13 { 14 private: 15 Singleton() 16 {} 17 static Singleton instance ; //靜態變量只會有一份數據存在 從而保證只有一個實例 18 public: 19 static Singleton* GetInstance() 20 { 21 return &instance; 22 } 23 }
總結:單例模式適用于只允許一個實例存在的情況,它的實現必須滿足三個條件,一是必須在類中就定義一個實例;二是必須有一個公有的靜態方法來獲取該實例;三是構造函數必須是私有的,來保證不容許別人通過調用構造函數來生成一個實例。在實現時要注意內存泄露問題,線程安全問題,性能問題。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。