單例模式:就是只有一個(gè)實(shí)例。
公司主營(yíng)業(yè)務(wù):成都網(wǎng)站制作、網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)推出稱多免費(fèi)做網(wǎng)站回饋大家。
singleton pattern單例模式:確保某一個(gè)類在程序運(yùn)行中只能生成一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。這個(gè)類稱為單例類。如一個(gè)工程中,數(shù)據(jù)庫訪問對(duì)象只有一個(gè),電腦的鼠標(biāo)只能連接一個(gè),操作系統(tǒng)只能有一個(gè)窗口管理器等,這時(shí)可以考慮使用單例模式。
眾所周知,c++中,類對(duì)象被創(chuàng)建時(shí),編譯系統(tǒng)為對(duì)象分配內(nèi)存空間,并自動(dòng)調(diào)用構(gòu)造函數(shù),由構(gòu)造函數(shù)完成成員的初始化工作,也就是說使用構(gòu)造函數(shù)來初始化對(duì)象。
1、那么我們需要把構(gòu)造函數(shù)設(shè)置為私有的 private,這樣可以禁止別人使用構(gòu)造函數(shù)創(chuàng)建其他的實(shí)例。
2、又單例類要一直向系統(tǒng)提供這個(gè)實(shí)例,那么,需要聲明它為靜態(tài)的實(shí)例成員,在需要的時(shí)候,才創(chuàng)建該實(shí)例。
3、且應(yīng)該把這個(gè)靜態(tài)成員設(shè)置為 null,在一個(gè)public 的方法里去判斷,只有在靜態(tài)實(shí)例成員為 null,也就是沒有被初始化的時(shí)候,才去初始化它,且只被初始化一次。
通常我們可以讓一個(gè)全局變量使得一個(gè)對(duì)象被訪問,但它不能阻止你實(shí)例化多個(gè)對(duì)象。如果采用全局或者靜態(tài)變量的方式,會(huì)影響封裝性,難以保證別的代碼不會(huì)對(duì)全局變量造成影響。
一個(gè)最好的辦法是,讓類自身負(fù)責(zé)保存它的唯一實(shí)例。這個(gè)類可以保證沒有其他實(shí)例可以被創(chuàng)建,并且它可以提供一個(gè)訪問該實(shí)例的方法,單例模式比全局對(duì)象好還包括,單例類可以繼承。
單例模式又分為兩種基本的情形:餓漢式和懶漢式
直接在靜態(tài)區(qū)初始化 instance,然后通過 get 方法返回,這樣這個(gè)類每次直接先生成一個(gè)對(duì)象,好像好久沒吃飯的餓漢子,急著吃飯一樣,急切的 new 對(duì)象,這叫做餓漢式單例類?;蛘呤窃?nbsp;get 方法中才 new instance,然后返回這個(gè)對(duì)象,和懶漢字一樣,不主動(dòng)做事,需要調(diào)用 get 方法的時(shí)候,才 new 對(duì)象,這就叫做懶漢式單例類。
如下是懶漢式單例類
1 //單例模式示例 2 class Singleton 3 { 4 public: 5 static Singleton * getInstance() 6 { 7 if (instance == NULL) { 8 instance = new Singleton(); 9 }10 11 return instance;12 }13 14 private:15 //私有的構(gòu)造函數(shù),防止外人私自調(diào)用16 Singleton()17 {18 cout << "實(shí)例化了" << count << "個(gè)對(duì)象!" << endl;19 count++;20 }21 //聲明一個(gè)靜態(tài)實(shí)例,靜態(tài)函數(shù)只能使用靜態(tài)的數(shù)據(jù)成員。整個(gè)類中靜態(tài)成員只有一個(gè)實(shí)例,通常在實(shí)現(xiàn)源文件中被初始化。22 static Singleton *instance;23 //記錄實(shí)例化的對(duì)象24 int count = 1;25 };26 27 Singleton * Singleton::instance = NULL;28 29 int main(void)30 {31 Singleton::getInstance();32 Singleton::getInstance();33 Singleton::getInstance();34 Singleton::getInstance();35 36 return 0;37 }
實(shí)例化了1個(gè)對(duì)象!
Program ended with exit code: 0
小結(jié):
懶漢式單例模式是用時(shí)間換取控件,餓漢式單例模式,是用空間換取時(shí)間。
繼續(xù)分析,考慮多線程下的懶漢式單例模式
上述代碼在單線程的情況下,運(yùn)行正常,但是遇到了多線程就出問題,假設(shè)有兩個(gè)線程同時(shí)運(yùn)行了這個(gè)單例類,同時(shí)運(yùn)行到了判斷 if 語句,并且當(dāng)時(shí),instance 實(shí)例確實(shí)沒有被初始化呢,那么兩個(gè)線程都會(huì)去運(yùn)行并創(chuàng)建實(shí)例,此時(shí)就不滿足單例類的要求了。那么我們需要寫上線程同步的功能。
1 //考慮到多線程情形下的單例模式 2 class Singleton 3 { 4 public: 5 //get 方法 6 static Singleton * getInstance(){ 7 //聯(lián)系互斥信號(hào)量機(jī)制,給代碼加鎖 8 lock(); 9 //判斷 null10 if (NULL == instance) {11 //判斷類沒有生成對(duì)象,才實(shí)例化對(duì)象,否則不再實(shí)例化12 instance = new Singleton();13 }14 //使用完畢,解鎖15 unlock();16 //返回一個(gè)實(shí)例化的對(duì)象17 return instance;18 }19 private:20 //聲明對(duì)象計(jì)數(shù)器21 int count = 0;22 //聲明一個(gè)靜態(tài)的實(shí)例23 static Singleton *instance;24 //私有構(gòu)造函數(shù)25 Singleton(){26 count++;27 cout << "實(shí)例化了" << count << "個(gè)對(duì)象!" << endl;28 }29 };30 //初始化 instance31 Singleton * Singleton::instance = NULL;
此時(shí),還是有 ab 兩個(gè)線程來運(yùn)行這個(gè)單例類,由于在同一時(shí)刻,只有一個(gè)線程能拿到同步鎖(互斥信號(hào)量機(jī)制),a 拿到了同步鎖,b 只能等待,如果 a發(fā)現(xiàn)實(shí)例還沒創(chuàng)建,a 就會(huì)創(chuàng)建一個(gè)實(shí)例,創(chuàng)建完畢,a 釋放同步鎖,然后 b 才能拿到同步鎖,繼續(xù)運(yùn)行接下來的代碼,b 發(fā)現(xiàn) a 線程運(yùn)行的時(shí)候,已經(jīng)生成了一個(gè)實(shí)例,b 線程就不會(huì)重復(fù)創(chuàng)建實(shí)例了,這樣就保證了我們?cè)诙嗑€程環(huán)境中只能得到一個(gè)實(shí)例。
繼續(xù)分析多線程下的懶漢式單例模式
代碼中,每次 get 方法中,得到 instance,都要判斷是否為空,且判斷是否為空之前,都要先加同步鎖,如果線程很多的時(shí)候,就要先等待加了同步鎖的線程運(yùn)行完畢,才能繼續(xù)判斷余下的線程,這樣就會(huì)造成大量線程的阻塞,且加鎖是個(gè)非常消耗時(shí)間的過程,應(yīng)該盡量避免(除非很有必要的時(shí)候)??尚械霓k法是,雙重判斷方法。
因?yàn)椋皇窃趯?shí)例還沒有創(chuàng)建的時(shí)候,需要加鎖判斷,保證每次只有一個(gè)線程創(chuàng)建實(shí)例,而當(dāng)實(shí)例已經(jīng)創(chuàng)建之后,其實(shí)就不需要加鎖操作了。
雙重判斷的線程安全的懶漢式單例模式
1 class Singleton 2 { 3 public: 4 //get 方法 5 static Singleton * getInstance(){ 6 //先判斷一次 null,只有 null 的時(shí)候需要加鎖,其他的時(shí)候,其實(shí)不需要加鎖 7 if (NULL == instance) { 8 //聯(lián)系互斥信號(hào)量機(jī)制,給代碼加鎖 9 lock();10 //然后再次判斷 null11 if (NULL == instance) {12 //判斷類沒有生成對(duì)象,才實(shí)例化對(duì)象,否則不再實(shí)例化13 instance = new Singleton();14 }15 //使用完畢,解鎖16 unlock();17 }18 //返回一個(gè)實(shí)例化的對(duì)象19 return instance;20 }21 private:22 //聲明對(duì)象計(jì)數(shù)器23 int count = 0;24 //聲明一個(gè)靜態(tài)的實(shí)例25 static Singleton *instance;26 //私有構(gòu)造函數(shù)27 Singleton(){28 count++;29 cout << "實(shí)例化了" << count << "個(gè)對(duì)象!" << endl;30 }31 };32 //初始化 instance33 Singleton * Singleton::instance = NULL;
這樣的雙重檢測(cè)機(jī)制,提高了單例模式在多線程下的效率,因?yàn)檫@樣的代碼,只需要在第一次創(chuàng)建實(shí)例的時(shí)候,需要加鎖,其他的時(shí)候,新航道雅思培訓(xùn)線程無需排隊(duì)等待加鎖之后,再去判斷了,比較高效。
再看餓漢式的單例模式,之前看了懶漢式的單例類,是線程不安全的,通過加鎖(雙重鎖),實(shí)現(xiàn)線程安全
回憶餓漢式單例類:直接在靜態(tài)區(qū)初始化 instance,然后通過 get 方法返回,這樣這個(gè)類每次直接先生成一個(gè)對(duì)象,好像好久沒吃飯的餓漢子,急著吃飯一樣,急切的 new 對(duì)象,這叫做餓漢式單例類。
1 class Singleton 2 { 3 public: 4 //get 方法 5 static Singleton * getInstance(){ 6 //返回一個(gè)實(shí)例化的對(duì)象 7 return instance; 8 } 9 private:10 //聲明一個(gè)靜態(tài)的實(shí)例11 static Singleton *instance;12 //私有構(gòu)造函數(shù)13 Singleton(){14 15 }16 };17 //每次先直接實(shí)例化instance,get 方法直接返回這個(gè)實(shí)例18 Singleton * Singleton::instance = new Singleton();
注意:靜態(tài)初始化實(shí)例可以保證線程安全,因?yàn)殪o態(tài)實(shí)例初始化在程序開始時(shí)進(jìn)入主函數(shù)之前,就由主線程以單線程方式完成了初始化!餓漢式的單例類,也就是靜態(tài)初始化實(shí)例保證其線程安全性,故在性能需求較高時(shí),應(yīng)使用這種模式,避免頻繁的鎖爭(zhēng)奪。
繼續(xù)看單例模式
上面的單例模式?jīng)]有 destory() 方法,也就是說,貌似上面的單例類沒有主動(dòng)析構(gòu)這個(gè)唯一實(shí)例!然而這就導(dǎo)致了一個(gè)問題,在程序結(jié)束之后,該單例對(duì)象沒有delete,導(dǎo)致內(nèi)存泄露!下面是一些大神的方法:一個(gè)妥善的方法是讓這個(gè)類自己知道在合適的時(shí)候把自己刪除,或者說把刪除自己的操作掛在操作系統(tǒng)中的某個(gè)合適的點(diǎn)上,使其在恰當(dāng)?shù)臅r(shí)候被自動(dòng)執(zhí)行。
我們知道,程序在結(jié)束的時(shí)候,系統(tǒng)會(huì)自動(dòng)析構(gòu)所有的全局變量。事實(shí)上,系統(tǒng)也會(huì)析構(gòu)所有的類的靜態(tài)成員變量,就像這些靜態(tài)成員也是全局變量一樣。如果在類的析構(gòu)行為中有必須的操作,比如關(guān)閉文件,釋放外部資源,那么上面的代碼無法實(shí)現(xiàn)這個(gè)要求。我們需要一種方法,正常的刪除該實(shí)例。利用這些特征,我們可以在單例類中定義一個(gè)這樣的靜態(tài)成員變量,而它的唯一工作就是在析構(gòu)函數(shù)中刪除單例類的實(shí)例。如下面的代碼中的Garbage類:
1 class Singleton 2 { 3 public: 4 //get 方法 5 static Singleton * getInstance(){ 6 //判斷單例否 7 if (NULL == instance) { 8 instance = new Singleton(); 9 }10 //返回一個(gè)實(shí)例化的對(duì)象11 return instance;12 }13 //c++ 嵌套的內(nèi)部類,作用是刪除單例類對(duì)象,Garbage被定義為Singleton的內(nèi)嵌類,以防該類被在其他地方濫用。14 class Garbage15 {16 public:17 ~Garbage(){18 if (Singleton::instance != NULL) {19 cout << "單例類的唯一實(shí)例被析構(gòu)了" << endl;20 delete Singleton::instance;21 }22 }23 };24 25 private:26 //單例類中聲明一個(gè)觸發(fā)垃圾回收類的靜態(tài)成員變量,它的唯一工作就是在析構(gòu)函數(shù)中刪除單例類的實(shí)例,利用程序在結(jié)束時(shí)析構(gòu)全局變量的特性,選擇最終的釋放時(shí)機(jī);27 static Garbage garbage;28 //聲明一個(gè)靜態(tài)的實(shí)例29 static Singleton *instance;30 //單例類的私有構(gòu)造函數(shù)31 Singleton(){32 cout << "調(diào)用了單例類的構(gòu)造函數(shù)" << endl;33 }34 //單例類的私有析構(gòu)函數(shù)35 ~Singleton(){36 cout << "調(diào)用了單例類的析構(gòu)函數(shù)" << endl;37 }38 };39 //初始化內(nèi)部的靜態(tài)變量,目睹是啟動(dòng)刪除的析構(gòu)函數(shù),如果不初始化,就不會(huì)被析構(gòu)40 //內(nèi)部類可以訪問外部類的私有成員,外部類不能訪問內(nèi)部類的私有成員!41 Singleton::Garbage Singleton::garbage;42 //初始化instance為 null43 Singleton * Singleton::instance = NULL;44 45 int main(void)46 {47 Singleton *a = Singleton::getInstance();48 Singleton *b = Singleton::getInstance();49 Singleton *c = Singleton::getInstance();50 51 if (a == b) {52 cout << "a = b" << endl;53 }54 55 return 0;56 }
調(diào)用了單例類的構(gòu)造函數(shù)
a = b
單例類的唯一實(shí)例被析構(gòu)了
調(diào)用了單例類的析構(gòu)函數(shù)
Program ended with exit code: 0
類Garbage被定義為Singleton的內(nèi)嵌類,以防該類在其他地方濫用,程序運(yùn)行結(jié)束時(shí),系統(tǒng)會(huì)調(diào)用Singleton的靜態(tài)成員garbage的析構(gòu)函數(shù),該析構(gòu)函數(shù)會(huì)刪除單例的唯一實(shí)例,使用這種方法釋放單例對(duì)象有以下特征:
1、在單例類內(nèi)部定義專有的嵌套類;
2、在單例類內(nèi)定義私有的專門用于釋放的靜態(tài)成員;
3、利用程序在結(jié)束時(shí)析構(gòu)全局變量的特性,選擇最終的釋放時(shí)機(jī);
4、使用單例的代碼不需要任何操作,不必關(guān)心對(duì)象的釋放。
其實(shí),繼續(xù)想單例類的實(shí)現(xiàn),有的人會(huì)這樣做:
在程序結(jié)束時(shí)調(diào)一個(gè)專門的方法,這個(gè)方法里判斷實(shí)例對(duì)象是否為 null,如果不為 null,就對(duì)返回的指針掉用delete操作。這樣做可以實(shí)現(xiàn)刪除單例的功能,但不僅很丑陋,而且容易出錯(cuò)。因?yàn)檫@樣的附加代碼很容易被忘記,而且也很難保證在delete之后,沒有代碼再調(diào)用GetInstance函數(shù)。不推薦直接的刪除方法。
繼續(xù)查看單例模式:?jiǎn)卫J皆趯?shí)際開發(fā)過程中是很有用的,單例模式的特征總結(jié):
1、一個(gè)類只有一個(gè)實(shí)例
2、提供一個(gè)全局訪問點(diǎn)
3、禁止拷貝
逐個(gè)分析:
1、實(shí)現(xiàn)只有一個(gè)實(shí)例,需要做的事情:將構(gòu)造函數(shù)聲明為私有
2、提供一個(gè)全局訪問點(diǎn),需要做的事情:類中創(chuàng)建靜態(tài)成員和靜態(tài)成員方法
3、禁止拷貝:把拷貝構(gòu)造函數(shù)聲明為私有,并且不提供實(shí)現(xiàn),將賦值運(yùn)算符聲明為私有,防止對(duì)象的賦值
完整的單例類實(shí)現(xiàn)代碼如下:
class Singleton {public: //get 方法 static Singleton * getInstance(){ if (NULL == instance) { lock(); //判斷單例否 if (NULL == instance) { instance = new Singleton(); } unlock(); } //返回一個(gè)實(shí)例化的對(duì)象 return instance; } //c++ 嵌套的內(nèi)部類,作用是刪除單例類對(duì)象,Garbage被定義為Singleton的私有內(nèi)嵌類,以防該類被在其他地方濫用。 class Garbage { public: ~Garbage(){ if (Singleton::instance != NULL) { cout << "單例類的唯一實(shí)例被析構(gòu)了" << endl; delete Singleton::instance; } } }; private: //單例類中定義一個(gè)這樣的靜態(tài)成員變量,而它的唯一工作就是在析構(gòu)函數(shù)中刪除單例類的實(shí)例,利用程序在結(jié)束時(shí)析構(gòu)全局變量的特性,選擇最終的釋放時(shí)機(jī); static Garbage garbage; //聲明一個(gè)靜態(tài)的實(shí)例 static Singleton *instance; //單例類的私有構(gòu)造函數(shù) Singleton(){ cout << "調(diào)用了單例類的構(gòu)造函數(shù)" << endl; } //單例類的私有析構(gòu)函數(shù) ~Singleton(){ cout << "調(diào)用了單例類的析構(gòu)函數(shù)" << endl; } //把拷貝構(gòu)造函數(shù)聲明為私有,就可以禁止外人拷貝對(duì)象,也不用實(shí)現(xiàn)它,聲明私有即可 Singleton(const Singleton ©); //把賦值運(yùn)算符重載為私有的,防止對(duì)象之間的賦值操作 Singleton & operator=(const Singleton &other); };//初始化內(nèi)部似有淚的靜態(tài)變量,目睹是啟動(dòng)刪除的析構(gòu)函數(shù),如果不初始化,就不會(huì)被析構(gòu)//內(nèi)部類可以訪問外部類的私有成員,外部類不能訪問內(nèi)部類的私有成員!Singleton::Garbage Singleton::garbage;//初始化instance為 nullSingleton * Singleton::instance = NULL;int main(void) { Singleton *a = Singleton::getInstance(); Singleton *b = Singleton::getInstance(); Singleton *c = Singleton::getInstance(); if (a == b) { cout << "a = b" << endl; } return 0; }
單例類de測(cè)試,兩個(gè)方法:
1、實(shí)例化多個(gè)對(duì)象,看調(diào)用了幾次構(gòu)造函數(shù),如果只調(diào)用一次,說明只創(chuàng)建一個(gè)實(shí)例
2、單步跟蹤,查看對(duì)象的地址,是否一樣,一樣則為一個(gè)對(duì)象
分享文章:軟件開發(fā)常用設(shè)計(jì)模式—單例模式總結(jié)(c++版)
當(dāng)前路徑:http://jinyejixie.com/article20/poesco.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、網(wǎng)站設(shè)計(jì)、企業(yè)建站、靜態(tài)網(wǎng)站、定制網(wǎng)站、響應(yīng)式網(wǎng)站
聲明:本網(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í)需注明來源: 創(chuàng)新互聯(lián)