总结C++单例模式
單例模式介紹
單例模式涉及到一個單一的類,該類負責創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
單例模式有三個關(guān)鍵點:
1、單例類只能有一個實例。
為此,單例類只能提供私有的構(gòu)造函數(shù),即保證不能隨意創(chuàng)建該類的實例。
2、單例類必須自己創(chuàng)建自己的唯一實例。
因為構(gòu)造函數(shù)是私有的,其他對象不能創(chuàng)建單例類的實例,只能是單例類自己來創(chuàng)建。
3、單例類必須給所有其他對象提供這一實例。
外界需要獲取并使用這個單例類的實例,但是由于該類的構(gòu)造函數(shù)是私有的,外界無法通過new去獲取它的實例,那么就必須提供一個靜態(tài)的公有方法,該方法創(chuàng)建或者獲取它本身的靜態(tài)私有對象并返回。
單例類主要解決了一個全局使用的類的頻繁的創(chuàng)建與銷毀。
所以單例模式有以下幾個優(yōu)點: 1、在內(nèi)存里只有一個實例,減少了內(nèi)存的開銷,尤其是頻繁的創(chuàng)建和銷毀實例;2、避免對資源的多重占用。
那單例模式有一個不好的地方就是,單例類沒有接口,不能繼承。
單例模式使用場景
單例模式就是一個類只能被實例化一次 ,也就是只能有一個實例化的對象的類。減少每次都new對象的開銷,節(jié)省系統(tǒng)資源,同時也保證了訪問對象的唯一實例。常用于如下場景:
1.頻繁實例化然后銷毀的對象。
2.創(chuàng)建對象太耗時,消耗太多資源,但又經(jīng)常用到。
單例模式分為懶漢式和餓漢式兩種。
懶漢式
懶漢式:故名思義,懶漢很懶只有餓了才會去找吃的。也就是說,只有在需要使用的時候才會去實例化。
線程不安全的懶漢式
代碼:
#include <iostream> #include <thread> #include <mutex>using namespace std;//常規(guī)的懶漢式單例模式,存在線程安全問題 class Singleton2 { public:static Singleton2* getInstance() {if (instance == NULL) {instance = new Singleton2();}return instance;}void test() {cout << "this is Singleton2..." << endl;}private:Singleton2() { cout << "Singleton2構(gòu)造函數(shù)" << endl; }; //構(gòu)造函數(shù)私有化Singleton2(const Singleton2&) {} //拷貝構(gòu)造函數(shù)私有化Singleton2& operator=(const Singleton2&) {} //賦值運算符私有化static Singleton2* instance; //靜態(tài)成員變量//類中嵌套類,用于自動回收資源class GC {public:~GC() {if (instance != NULL) {delete instance;instance = NULL;cout << "GC2自動回收..." << endl;}cout << "GC2..." << endl;}};static GC gc; }; //靜態(tài)成員變量,類內(nèi)定義,類外初始化 Singleton2* Singleton2::instance = NULL; Singleton2::GC Singleton2::gc;int main() {Singleton2::getInstance()->test();return 0; }上面的代碼是線程不安全的單例模式。因為,如果兩個線程同時獲取單例類的實例,都發(fā)現(xiàn)實例不存在,因此都會進行實例化,就會產(chǎn)生兩個實例都要賦值給instance。
這里的線程安全指的是:一個線程在初始化某個變量的時候,其他線程執(zhí)行到這個變量的初始化的時候,就會掛起。就是說只有一個線程會執(zhí)行該變量的初始化
單例類中嵌套回收的類的作用是:不用再手動調(diào)用析構(gòu)函數(shù)了。程序退出release對象析構(gòu)時,單例的指針也會隨著析構(gòu)。
結(jié)果:
為了解決上述問題,設(shè)計出線程安全的懶漢式單例模式,需要考慮加鎖。當然也可以不加鎖,使用靜態(tài)局部變量。
線程安全的懶漢式有兩種實現(xiàn)方式,一種是使用靜態(tài)局部變量,另一種是加鎖。
使用靜態(tài)局部變量(C++11)
由于C++11中規(guī)定靜態(tài)成員變量就是線程安全的,所以這種寫法不存在線程安全問題。
原理就是函數(shù)的局部靜態(tài)變量生命周期隨著進程結(jié)束而結(jié)束。
結(jié)果:
加鎖的懶漢式
#include <iostream> #include <thread> #include <mutex>using namespace std;//加鎖,解決常規(guī)的懶漢式單例模式,存在線程安全的問題 class Singleton3 { public:static Singleton3* getinstance() {//兩個NULL可以提高獲取實例效率if (instance == NULL) {mtx.lock();if (instance == NULL) {instance = new Singleton3();}mtx.unlock();}return instance;}void test() {cout << "this is Singleton3..." << endl;}private:Singleton3() { cout << "Singleton3構(gòu)造函數(shù)" << endl; }Singleton3(const Singleton3&) {}Singleton3& operator=(const Singleton3&) {}static Singleton3* instance;static mutex mtx; //鎖//類中嵌套類,用于自動回收資源class GC {public:~GC() {if (instance != NULL) {delete instance;instance = NULL;cout << "GC3自動回收..." << endl;}cout << "GC3..." << endl;}};static GC gc; }; Singleton3* Singleton3::instance = NULL; mutex Singleton3::mtx; Singleton3::GC Singleton3::gc;int main() {Singleton3::getinstance()->test();return 0; }結(jié)果:
餓漢式
餓漢式:餓了肯定要饑不擇食。在單例類定義的時候就進行實例化。
餓漢式?jīng)]有線程安全問題。
代碼:
結(jié)果:
c++單例模式為什么不在析構(gòu)函數(shù)中釋放靜態(tài)的單例對象
1、單例中的 new 的對象需要delete釋放。
2、delete釋放對象的時候才會調(diào)用對象的析構(gòu)函數(shù)。
3、如果在析構(gòu)函數(shù)里調(diào)用delete,那么程序結(jié)束時,根本進不去析構(gòu)函數(shù),怎么會delete。
4、如果程序結(jié)束能自動析構(gòu),那么就會造成一個析構(gòu)的循壞,所以new對應(yīng)于delete。
經(jīng)過驗證,如果不手動delete對象,符合第3條,因為只有進行了delete的時候才會執(zhí)行析構(gòu)函數(shù),但是delete是在析構(gòu)函數(shù)中執(zhí)行的,不在外部手動delete,則不會執(zhí)行析構(gòu)函數(shù),所以在析構(gòu)函數(shù)中釋放對象也沒有用。
但是,如果手動delete對象,將會出現(xiàn)死循環(huán),也就是出現(xiàn)了第4條的情況。因為手動delete會調(diào)用析構(gòu)函數(shù),而在析構(gòu)函數(shù)中,又使用了delete靜態(tài)的單例對象,進而又會調(diào)用析構(gòu)函數(shù),這樣就會一直循環(huán)下去,直到棧溢出。
綜上所述,不可以在析構(gòu)函數(shù)中釋放靜態(tài)的單例對象。可以在類中嵌套一個回收的類,用于釋放單例的對象。
如果不在析構(gòu)函數(shù)中釋放靜態(tài)的單例對象,而在外部手動使用delete,會釋放單例的對象嗎?
答案:不會
例如如下代碼:
結(jié)果:
從結(jié)果中可以看出,把Singleton2::getInstance()賦值給S2之后,S2就指向了單例對象的那塊內(nèi)存,但是
即使把S2 delete掉之后,單例對象還是存在的。這有點像與智能指針,總之,無法通過外部的delete刪除單例對象,所以只能在單例類內(nèi)部使用嵌套類來刪除單例對象,防止內(nèi)存泄漏。
參考鏈接:
C++ 單例模式 - 泣血 - 博客園 (cnblogs.com)
C++實現(xiàn)單例模式_Elena-N的博客-CSDN博客_單例模式c++實現(xiàn)
【設(shè)計模式】單例模式,嵌套類實現(xiàn)釋放對象內(nèi)存 - 走看看 (zoukankan.com)
C++單例模式為何要實例化一個對象不全部使用static_C 語言_腳本之家 (jb51.net)
C++單例模式 正確的資源回收方式_ice_ly000的博客-CSDN博客
總結(jié)
- 上一篇: vi使用手册(zt)
- 下一篇: vc.60写c语言,悟空问答如何用VC6