C 基礎之單例模式自動釋放

2021-10-21 08:19:45 字數 4625 閱讀 2468

個人學習筆記

之前的介紹過關於單例模式的例子,其中物件是由_pinstance指標來儲存,通過new建立的物件並沒有進行釋放,是因為單例模式之後沒有其他**需要執行,程式會立馬結束,作業系統會自行**相關資源,但對於後面有相關**需要執行的程式來說,這種操作就會造成記憶體洩漏。有沒有什麼方式可以讓物件自動釋放?這樣既可以避免記憶體洩漏,又可以讓程式設計師不用關注物件釋放的問題。在涉及到自動的問題時,我們很自然的可以想到:物件被銷毀時,會自動呼叫其析構函式。利用這一特性,我們可以解決這一問題,這裡介紹4種方式對單例模式進行自動釋放:友元形式、內部類加靜態資料成員形式、atexit形式、pthread_once形式。

建立乙個新類autorelease,作為singleton的友元,建立該類的棧物件,由於編譯器會自動釋放棧物件,在釋放棧物件時會執行析構函式,這樣執行析構函式的同時順便把單例模式的物件給釋放了,這樣就可以達到目的了。

**如下(示例):

#include

using std::cout;

using std::endl;

class

autorelease

;//1、友元形式

class

singleton

return _pinstance;

}//不需要主動呼叫來釋放物件了

static

void

destroy()

}private

:singleton()

~singleton()

private

:static singleton * _pinstance;};

class

autorelease

~autorelease()

}};singleton *singleton::_pinstance =

nullptr

;int

main()

除了友元,還可以把autorelease作為singleton的內部類,用public修飾時,可以直接在外面建立autorelease的棧物件,其作用原理與友元形式類似,因為autorelease只為singleton服務,所以autorelease要用private修飾,用private修飾後,autorelease物件就不能在singleton類外面建立,所以只能在singleton類裡面建立,作為singleton的普通資料成員,在singleton裡建立autorelease物件時,會呼叫autorelease建構函式,而此時的autorelease物件並不是棧物件,所以不會自動執行析構函式,需要singleton建立的類物件銷毀時才會**資料成員,這時才會執行autorelease的析構函式,進而再**singleton的物件,而singleton建立的單例物件就是靠autorelease才能釋放掉,這裡就陷入了乙個死迴圈,所以就不能建立autorelease物件作為singleton的普通資料成員,也就是說singleton物件不能擁有autorelease物件作為普通資料成員,所以需要把autorelease建立的物件從singleton建立的物件中拿出來,放到全域性/靜態區的位置,也就是讓autorelease建立的物件作為singleton的靜態資料成員,不僅僅被某個singleton物件擁有,它是乙個全域性的,不受某乙個singleton物件控制,又因為處於全域性/靜態區的物件或變數會隨著程序的結束而進行銷毀,這時就會執行autorelease的析構函式,進而把_pinstance給delete掉,這時就會執行singleton的析構函式,銷毀釋放singleton單例模式建立的物件,最終達到目的。

**如下(示例):

#include

using std::cout;

using std::endl;

class

autorelease

;//2、內部類 + 靜態資料成員

class

singleton

return _pinstance;

}static

void

destroy()

}private

:class

autorelease

~autorelease()

}};private

:singleton()

~singleton()

private

:static singleton * _pinstance;

static autorelease _ar;

//不是棧物件};

singleton *singleton::_pinstance =

nullptr

;singleton::autorelease singleton::_ar;

//放到全域性靜態的位置,編譯器會自動賦乙個初值

intmain()

這裡需要借助乙個c的函式atexit(),atexit函式會註冊乙個函式,註冊的函式會在程序正常結束的時候呼叫,通過atexit註冊多次就會呼叫多次。這時需要考慮atexit的位置,考慮到singleton單例模式建立的物件只能被銷毀一次,所以atexit()最好是放到單例模式建立的作用域內。但是在多執行緒環境下,上面的**都是有缺陷的,因為if(nullptr==_pinstance)判斷語句對於有幾個執行緒的情況下,可能會同時滿足if判斷語句中的內容,然後建立多個singleton物件,這樣不僅不符合單例模式(乙個類有且只能有乙個物件),而且釋放的時候不會把多執行緒建立的多個物件全部銷毀釋放掉,從而造成記憶體洩漏,這種情況下,對於多執行緒環境下是不安全的。出現這種情況是因為,在進入if判斷語句之前,_pinstance的值為空,所以這裡我們可以在_pinstance初始化的時候賦乙個值,這樣下次進入getinstance()時,_pinstance已經有值了,即使在多執行緒環境下,也不會建立多個物件,這樣解決了多執行緒環境下不安全的問題,這就對應一種模式:餓漢模式。

**如下(示例):

#include

#include

using std::cout;

using std::endl;

class

autorelease

;//3、atexit + 餓漢模式

//可以解決多執行緒環境下不安全的問題

class

singleton

return _pinstance;

}static

void

destroy()

}private

:singleton()

~singleton()

private

:static singleton * _pinstance;};

/* singleton *singleton::_pinstance = nullptr;//飽漢模式(懶漢模式) */

singleton *singleton::_pinstance =

getinstance()

;//餓漢模式

intmain()

其實在linux系統中,有乙個函式可以保證多執行緒環境下安全性的問題,它就是pthread_once(),完整形式:int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)),該函式使用初值為pthread_once_init的once_control變數保證init_routine()函式在本程序執行序列中僅執行一次。在多執行緒程式設計環境下,儘管pthread_once()呼叫會出現在多個執行緒中,但init_routine()函式僅執行一次,究竟在哪個執行緒中執行是不定的,是由核心排程來決定。所以在這種情況下,即使在多執行緒環境下,選擇飽漢模式或者餓漢模式都都可以,因為init_routine()函式只會執行一次。注意一點該形式只能在linux系統下使用,跨平台性不高。在linux系統通過g++編譯的時候需要加上執行緒庫-lpthread,其中這個p是posix,它是linux下的乙個標準。

**如下(示例):

#include

#include

#include

using std::cout;

using std::endl;

class

autorelease

;//4、pthread_once形式

//可以解決多執行緒環境下不安全的問題

class

singleton

static

void

init()

static

void

destroy()

}private

:singleton()

~singleton()

private

:static singleton * _pinstance;

static pthread_once_t _once;};

singleton *singleton::_pinstance =

nullptr

;//飽漢模式(懶漢模式)

/* singleton *singleton::_pinstance = getinstance();//餓漢模式 */

pthread_once_t singleton::_once = pthread_once_init;

intmain()

單例模式的自動釋放

一般來說,程式退出的時候,所有的資源都被 但是在使用檢測記憶體洩露的工具比如valgrind時會被檢測出來,可以理解為這是一種 假的記憶體洩漏 為此,這裡分別使用三種方法來避免這個問題。實現 1 include 2 using std cout 3 using std endl 45 class s...

c 單例模式的自動釋放問題

1.巢狀類 靜態物件 include using std cout using std endl class singleton return pinstance private class autorelease autorelease private singleton singleton pr...

C 單例模式物件釋放的問題

std mutex resource mutex class mycas 這是乙個單例類 私有化建構函式 private static mycas m instance 靜態成員變數 public static mycas getinstance return m instance class cg...