- 前言
- 無法調(diào)用析構(gòu)函數(shù)的原因
-
改進(jìn)方法
- 內(nèi)嵌回收類
- 智能指針
- 局部靜態(tài)變量
- 參考文章
前言
在《單例模式學(xué)習(xí)》中提到了,在單例對象是通過new
關(guān)鍵字動態(tài)分配在堆上的情況下,當(dāng)程序退出時,不會通過C++的RAII機(jī)制自動調(diào)用其析構(gòu)函數(shù)。本文討論一下這種現(xiàn)象的原因以及解決方法。
無法調(diào)用析構(gòu)函數(shù)的原因
在DCLP(雙檢查鎖模式)中,CSingleton中的instance
是一個靜態(tài)指針變量,被分配在全局/靜態(tài)存儲區(qū)。而instance
所指向的CSingleton實(shí)例是通過new
創(chuàng)建在堆上的,只能手動調(diào)用delete來釋放相關(guān)資源(對于單例模式這是無法實(shí)現(xiàn)的,因?yàn)槲鰳?gòu)函數(shù)私有),無法通過RAII釋放相關(guān)資源。
在程序結(jié)束時,instance
這個指針變量被銷毀了,但它所指向的內(nèi)存空間中的CSingleton對象并沒有被顯式銷毀,而是由操作系統(tǒng)去回收這一塊內(nèi)存(不會調(diào)用其析構(gòu)函數(shù))。然而依賴操作系統(tǒng)來清理資源并不是一個優(yōu)雅的結(jié)束方式,可能會造成文件句柄未關(guān)閉、網(wǎng)絡(luò)連接未斷開等資源泄漏。
class CSingleton
{
public:
static CSingleton* getInstance();
static std::mutex mtx;
private:
CSingleton(){}
~CSingleton(){}
CSingleton(const CSingleton&) = delete;
CSingleton& operator=(const CSingleton&) = delete;
static CSingleton* instance;
};
CSingleton* CSingleton::instance;
CSingleton* CSingleton::getInstance()
{
if(nullptr == instance)
{
mtx.lock();
if(nullptr == instance)
{
instance = new CSingleton();
}
mtx.unlock();
}
return instance;
}
改進(jìn)方法
在討論改進(jìn)方法時,我們還是傾向于利用C++的RAII機(jī)制,而不是手動去控制釋放的時機(jī)。
內(nèi)嵌回收類
我們的單例類對象生命周期的開始是在第一次調(diào)用時,結(jié)束是在程序結(jié)束時。
而且我們知道①靜態(tài)成員變量的生命周期是從程序啟動到結(jié)束②在靜態(tài)成員變量被銷毀時會調(diào)用其析構(gòu)函數(shù)
因此我們可以在單例類中定義一個用于釋放單例類資源的內(nèi)嵌類,將其析構(gòu)函數(shù)定義為顯式刪除單例對象的操作,然后在單例類中添加一個內(nèi)嵌類類型的靜態(tài)成員變量garbo
。
這樣的話,在程序結(jié)束時garbo
就會被銷毀,而RAII機(jī)制確保了在銷毀時會調(diào)用內(nèi)嵌類CGarbo
的析構(gòu)函數(shù)。
因?yàn)樵?code>~CGarbo()中delete了CSingleton::instance
,所以~CSingleton()
就會被調(diào)用,相關(guān)資源得以釋放。
class CSingleton
{
public:
static CSingleton* getInstance();
private:
CSingleton(){std::cout<<"創(chuàng)建了一個對象"<< std::endl;}
~CSingleton(){std::cout<<"銷毀了一個對象"<< std::endl;}
CSingleton(const CSingleton&) = delete;
CSingleton& operator=(const CSingleton&) = delete;
static CSingleton* instance;
static std::mutex mtx;
class CGarbo
{
public:
CGarbo(){}
~CGarbo()
{
if(nullptr != CSingleton::instance) //嵌套類可訪問外層類的私有成員
{
delete CSingleton::instance;
instance = nullptr;
}
std::cout<<"Garbo worked"<< std::endl;
}
};
static CGarbo garbo; //定義一個靜態(tài)成員,程序結(jié)束時,系統(tǒng)會自動調(diào)用它的析構(gòu)函數(shù)
};
CSingleton* CSingleton::instance;
std::mutex CSingleton::mtx;
CSingleton* CSingleton::getInstance()
{
...
}
CSingleton::CGarbo CSingleton::garbo; //還需要初始化一個垃圾清理的靜態(tài)成員變量
運(yùn)行結(jié)果:
智能指針
我們還可以利用智能指針引用計數(shù)機(jī)制,對資源自動管理:
//編譯不通過
class CSingleton
{
public:
static std::shared_ptr<CSingleton> getInstance();
private:
CSingleton(){std::cout<<"創(chuàng)建了一個對象"<<std::endl;}
~CSingleton(){std::cout<<"銷毀了一個對象"<<std::endl;}
CSingleton(const CSingleton&) = delete;
CSingleton& operator=(const CSingleton&) = delete;
static std::shared_ptr<CSingleton> instance;
static std::mutex mutex;
};
std::shared_ptr<CSingleton> CSingleton::instance;
std::mutex CSingleton::mutex;
std::shared_ptr<CSingleton> CSingleton::getInstance()
{
if (nullptr == instance)
{
std::lock_guard<std::mutex> lock(mutex);
if (nullptr == instance)
{
instance = std::shared_ptr<CSingleton>(new CSingleton());
}
}
return instance;
}
注意上述代碼無法通過編譯,原因是當(dāng)std::shared_ptr
被銷毀時,它會嘗試使用delete來銷毀管理的對象。但因?yàn)镃Singleton的析構(gòu)函數(shù)是私有的,所以無法從外部手動銷毀CSingleton實(shí)例。
要解決這個問題,我們需要在CSingleton中自定義一個刪除器,讓std::shared_ptr
能夠調(diào)用私有析構(gòu)函數(shù)。
class CSingleton
{
public:
static std::shared_ptr<CSingleton> getInstance();
private:
CSingleton(){std::cout<<"創(chuàng)建了一個對象"<<std::endl;}
~CSingleton(){std::cout<<"銷毀了一個對象"<<std::endl;}
CSingleton(const CSingleton&) = delete;
CSingleton& operator=(const CSingleton&) = delete;
static std::shared_ptr<CSingleton> instance;
static std::mutex mutex;
static void deleter(CSingleton* p); //自定義刪除器
};
std::shared_ptr<CSingleton> CSingleton::instance;
std::mutex CSingleton::mutex;
std::shared_ptr<CSingleton> CSingleton::getInstance()
{
if (nullptr == instance)
{
std::lock_guard<std::mutex> lock(mutex);
if (nullptr == instance)
{
instance = std::shared_ptr<CSingleton>(new CSingleton(),CSingleton::deleter);
}
}
return instance;
}
void CSingleton::deleter(CSingleton* p)
{
delete p;
std::cout<<"deleter worked"<<std::endl;
}
測試結(jié)果:文章來源:http://www.zghlxwxcb.cn/news/detail-841334.html
局部靜態(tài)變量
局部靜態(tài)變量形式的單例模式也可以完成資源的釋放,詳見《單例模式學(xué)習(xí)》。文章來源地址http://www.zghlxwxcb.cn/news/detail-841334.html
static CSingleton& getInstance()
{
static CSingleton instance;
return instance;
}
參考文章
到了這里,關(guān)于設(shè)計模式學(xué)習(xí)(一)單例模式補(bǔ)充——單例模式析構(gòu)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!