一.概述:
創(chuàng)新互聯(lián)專注于企業(yè)成都全網(wǎng)營(yíng)銷、網(wǎng)站重做改版、龍華網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5響應(yīng)式網(wǎng)站、電子商務(wù)商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為龍華等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
因?yàn)樵谠O(shè)計(jì)或開發(fā)中,肯定會(huì)有這么一種情況,一個(gè)類只能有一個(gè)對(duì)象被創(chuàng)建,如果有多個(gè)對(duì)象的話,可能會(huì)導(dǎo)致狀態(tài)的混亂和不一致。這種情況下,單例模式是最 恰當(dāng)?shù)慕鉀Q辦法。有很多地方需要這樣的功能模塊,如系統(tǒng)的日志輸出,GUI應(yīng)用必須是單鼠標(biāo),MODEM的聯(lián)接需要一條且只需要一條電話線,操作系統(tǒng)只能有一個(gè)窗口管理器,一臺(tái)PC連一個(gè)鍵盤。單例模式有很多種實(shí)現(xiàn)方式,各自的特性不相同,使用的情形也不相同。今天要實(shí)現(xiàn)的是常用的三種,分別是餓漢式、懶漢式和多線程式。
《設(shè)計(jì)模式》一書中的實(shí)現(xiàn)有三個(gè)要素,定義一個(gè)單例類,要使用類的私有靜態(tài)指針變量指向類的唯一實(shí)例(即在類中就生成一個(gè)對(duì)象),并用一個(gè)公有的靜態(tài)方法獲取該實(shí)例,并把構(gòu)造函數(shù)定義為protected或private。
二.懶漢式實(shí)現(xiàn)單例模式:
懶漢式的特點(diǎn)是延遲加載,懶漢么,很懶,它只在要用到實(shí)例時(shí)才加載實(shí)例。
/**************************************** 2 > File Name:lanhan.cpp 3 > Author:xiaoxiaohui 4 > mail:1924224891@qq.com 5 > Created Time:2016年05月07日 星期六 15時(shí)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; //靜態(tài)的 私有的 17 public: 18 static Singleton* GetInstace() 19 { 20 if(_instance == NULL) 21 { 22 _instance = new Singleton(); 23 } 24 return _instance; //如果非空則new一個(gè)對(duì)象 后者返回原來(lái)的兩個(gè)對(duì)象(所以保證了只有一個(gè)對(duì)象生成) 25 } 26 27 } 28
上面的這一實(shí)現(xiàn)存在內(nèi)存泄露問題,因?yàn)闆]有釋放_(tái)instance指針,下面為懶漢式的改進(jìn)版:
8 #include<iostream> 9 using namespace std 10 11 class Singleton 12 { 13 private: 14 Singleton() 15 {} 16 static Singleton* _instance; //靜態(tài)的 私有的 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; //靜態(tài)變量會(huì)在程序結(jié)束時(shí)調(diào)用它的析構(gòu)函數(shù) 31 public: 32 static Singleton* GetInstance() 33 { 34 if(_instance == NULL) 35 { 36 _instance = new Singleton(); 37 } 38 return _instance; //如果非空則new一個(gè)對(duì)象 后者返回原來(lái)的兩個(gè)對(duì)象(所以保證了只有一個(gè)對(duì)象生成) 39 } 40 41 }
該實(shí)現(xiàn)會(huì)在程序結(jié)束時(shí)調(diào)用靜態(tài)變量的析構(gòu)函數(shù),從而delete了唯一的Singleton對(duì)象。
使用這種方法釋放單例對(duì)象有以下特征:
1.在單例類內(nèi)部定義專有的嵌套類。
2.在單例類內(nèi)定義私有的專門用于釋放的靜態(tài)成員。
3.利用程序在結(jié)束時(shí)析構(gòu)全局變量的特性,選擇最終的釋放時(shí)機(jī)。
但是現(xiàn)在還有問題,如果在多線程環(huán)境下,因?yàn)椤癷f(_instance == NULL)”并不是原子的,會(huì)存在線程安全問題(如果一個(gè)線程剛剛判斷了指針為空,這時(shí)另一個(gè)線程的優(yōu)先級(jí)更高或者其它原因,打斷了原來(lái)線程的執(zhí)行,再次判斷指針也會(huì)為空,所以會(huì)出現(xiàn)兩個(gè)實(shí)例)下面為多線程環(huán)境下的懶漢式單例模式:
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; //靜態(tài)的 私有的 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; //靜態(tài)變量會(huì)在程序結(jié)束時(shí)調(diào)用它的析構(gòu)函數(shù) 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一個(gè)對(duì)象 后者返回原來(lái)的兩個(gè)對(duì)象(所以保證了只有一個(gè)對(duì)象生成) 45 } 46 47 } 48
但現(xiàn)在還有問題,當(dāng)有大量的線程時(shí),只會(huì)有一個(gè)線程進(jìn)入互斥鎖,然后執(zhí)行下面的代碼而其它線程只能等待,并且加鎖是一個(gè)繁重的過(guò)程,這樣會(huì)導(dǎo)致加很多次鎖,這樣就太不高效了。下面是高效版的多線程環(huán)境下的懶漢式單例模式:
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; //靜態(tài)的 私有的 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; //靜態(tài)變量會(huì)在程序結(jié)束時(shí)調(diào)用它的析構(gòu)函數(shù) 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一個(gè)對(duì)象 后者返回原來(lái)的兩個(gè)對(duì)象(所以保證了只有一個(gè)對(duì)象生成) 48 } 49 50 }
這樣只有沒有Singleton實(shí)例時(shí)才會(huì)進(jìn)入加鎖的代碼,而當(dāng)有Singleton實(shí)例時(shí)不需要進(jìn)入加鎖的代碼中,直接返回已存在的實(shí)例就行了。
三.餓漢式的單例模式:在一開始就創(chuàng)建實(shí)例,要用時(shí)直接返回即可。餓汗式的單例模式?jīng)]有線程安全問題,因?yàn)樗跃€程都只能訪問一個(gè)已存在的對(duì)象,無(wú)論線程怎么調(diào)度都不會(huì)有多個(gè)對(duì)象出現(xiàn)。因?yàn)閷?duì)象是一個(gè)靜態(tài)變量(不是指針),會(huì)在程序結(jié)束時(shí)自動(dòng)調(diào)用它的析構(gòu)函數(shù),所以不用考慮內(nèi)存泄露問題。
餓汗式的特點(diǎn):代碼簡(jiǎn)單,不會(huì)出現(xiàn)內(nèi)存泄露,是線程安全的。
1 /**************************************** 2 > File Name:erhan.cpp 3 > Author:xiaoxiaohui 4 > mail:1924224891@qq.com 5 > Created Time:2016年05月07日 星期六 16時(shí)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 ; //靜態(tài)變量只會(huì)有一份數(shù)據(jù)存在 從而保證只有一個(gè)實(shí)例 18 public: 19 static Singleton& GetInstance() 20 { 21 return instance; 22 } 23 }
聲明一個(gè)局部的靜態(tài)變量,而靜態(tài)變量在全局范圍內(nèi)只有一份數(shù)據(jù),所以無(wú)論調(diào)用多少此GetInstance,返回的都是那一個(gè)實(shí)例。
但這個(gè)實(shí)現(xiàn)存在問題,Singleton singleton = Singleton :: GetInstance(),這么做就出現(xiàn)了一個(gè)類拷貝的問題,這就違背了單例的特性。產(chǎn)生這個(gè)問題原因在于:因?yàn)樵谶@里沒有實(shí)現(xiàn)拷貝構(gòu)造函數(shù),編譯器會(huì)為類生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù),來(lái)支持類的拷貝。
解決方法:1.自己再定義一個(gè)拷貝構(gòu)造函數(shù)和operator=,這個(gè)拷貝構(gòu)造函數(shù)和operator=什么都不做。
2.返回一個(gè)Singleton指針。
下面為方法2的代碼:
8 #include<iostream> 9 using namespace std 10 11 12 class Singleton 13 { 14 private: 15 Singleton() 16 {} 17 static Singleton instance ; //靜態(tài)變量只會(huì)有一份數(shù)據(jù)存在 從而保證只有一個(gè)實(shí)例 18 public: 19 static Singleton* GetInstance() 20 { 21 return &instance; 22 } 23 }
總結(jié):?jiǎn)卫J竭m用于只允許一個(gè)實(shí)例存在的情況,它的實(shí)現(xiàn)必須滿足三個(gè)條件,一是必須在類中就定義一個(gè)實(shí)例;二是必須有一個(gè)公有的靜態(tài)方法來(lái)獲取該實(shí)例;三是構(gòu)造函數(shù)必須是私有的,來(lái)保證不容許別人通過(guò)調(diào)用構(gòu)造函數(shù)來(lái)生成一個(gè)實(shí)例。在實(shí)現(xiàn)時(shí)要注意內(nèi)存泄露問題,線程安全問題,性能問題。
文章名稱:c++單例模式---超詳細(xì)
URL網(wǎng)址:http://jinyejixie.com/article40/psioho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)、微信小程序、搜索引擎優(yōu)化、App設(shè)計(jì)、面包屑導(dǎo)航、定制開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)