C++知識分享:為什么單例代碼的析構(gòu)函數(shù)有時候不執(zhí)行呢?
寫C++的時候用到單例,于是很自然的寫出如下的代碼:

代碼的本意:靜態(tài)成員函數(shù)getInstance獲取單例指針,并且在析構(gòu)函數(shù)中做一些收尾工作。
運行代碼后發(fā)現(xiàn)析構(gòu)函數(shù)死活不執(zhí)行,難道一個單例模式都能寫錯?反復(fù)確認,沒發(fā)現(xiàn)問題所在,于是上萬能的StackOverflow上找原因。正好有伙計有同樣的疑惑,有哥們給出了一個可行的方案。根據(jù)其答案修改代碼如下:

對比前一段代碼,主要改動是移除了靜態(tài)指針成員,改用函數(shù)內(nèi)的靜態(tài)成員。由于_instance是函數(shù)內(nèi)的靜態(tài)成員,在首次調(diào)用時被初始化(感謝無參構(gòu)造函數(shù)),之后調(diào)用將略過初始化而執(zhí)行后續(xù)代碼;函數(shù)返回實例的引用,故而每次調(diào)用得到的是同一個對象,達到了單例的目的;程序執(zhí)行結(jié)束后,實例的析構(gòu)函數(shù)被自動調(diào)用,析構(gòu)函數(shù)中的代碼正確執(zhí)行。
問題解決了,但什么原因造成第一段單例代碼的析構(gòu)函數(shù)不執(zhí)行呢?
這是由于C++持有對象的方式造成的(或者說C++允許程序員手動控制內(nèi)存引起)。Java/PHP等帶有回收機制的語言,持有對象的方式是通過指針,程序員申請對象后會自動分配內(nèi)存,系統(tǒng)負責(zé)跟蹤和回收無用的對象和存在。C++允許開發(fā)人員以變量的方式持有對象,例如:Foo?foo?[=?Foo(args)]。變量初始化后獲得對象的引用,離開作用域后,系統(tǒng)銷毀執(zhí)行棧,對象自動被析構(gòu)。C++也可以以指針的形式獲得對象的引用:?Foo*?foo?=?new?Foo(args)。這種方式分配的對象,需要開發(fā)人員手動管理。如果不執(zhí)行delete,對象和分配的內(nèi)存將一直存在,直到程序退出后,才由操作系統(tǒng)回收。如下代碼可說明這點:

從上面的代碼可以看出,對象沒有引用計數(shù)的情況下,編譯器和系統(tǒng)不敢隨便回收new出來的對象內(nèi)存:多個指針指向同一個對象,delete了對象可能會導(dǎo)致其他代碼崩潰;釋放內(nèi)存后,其他指針再delete會多次delete同一塊內(nèi)存,引發(fā)不可預(yù)知風(fēng)險。
綜上,指針單例析構(gòu)函數(shù)沒有被調(diào)用的原因是: 自己new的對象,需要自己delete,別指望別人幫你正確調(diào)用析構(gòu)函數(shù)。
問題的解決方案有以下幾種:
(1)同StackOverflow上回答,改用變量方式持有單例對象。程序運行結(jié)束前會銷毀所有變量,變量的析構(gòu)函數(shù)將被正確調(diào)用;
(2)在main函數(shù)退出前delete單例。例如增加一個destroy的靜態(tài)成員函數(shù),將指針指向的對象銷毀;
(3)使用auto_ptr/unique_ptr等智能指針。
如有其它解決方案,歡迎交流指正!
那么今天的分享就到這里了,后續(xù)會更新更多精彩項目或者知識內(nèi)容的,大家要好好學(xué)C語言C++喲~

寫在最后:對于準備學(xué)習(xí)C/C++編程的小伙伴,如果你想更好的提升你的編程核心能力(內(nèi)功)不妨從現(xiàn)在開始!
微信公眾號:C語言編程學(xué)習(xí)基地
整理分享(多年學(xué)習(xí)的源碼、項目實戰(zhàn)視頻、項目筆記,基礎(chǔ)入門教程)
歡迎轉(zhuǎn)行和學(xué)習(xí)編程的伙伴,利用更多的資料學(xué)習(xí)成長比自己琢磨更快哦!
