C++面向?qū)ο缶幊讨鰳?gòu)詳解
1. 概述
類的析構(gòu)函數(shù)執(zhí)行與構(gòu)造函數(shù)相反的操作,當對象結(jié)束其生命周期,程序就會自動執(zhí)行析構(gòu)函數(shù):
class ImageEx { public: ImageEx() { cout << "Execute the constructor!" << endl; } ~ImageEx() { cout << "Execute the destructor!" << endl; } }; int main() { ImageEx imageEx; return 0; }
那么同樣的問題來了,為什么要有析構(gòu)函數(shù)呢?
2. 詳論
2.1. 對象生命周期
在經(jīng)典C++中,需要通過new/delete來手動管理動態(tài)內(nèi)存。如果我們在類中申請一個動態(tài)數(shù)組,并且通過自定義的函數(shù)Release()來釋放它:
class ImageEx { public: ImageEx() { cout << "Execute the constructor!" << endl; data = new unsigned char[10]; } ~ImageEx() { cout << "Execute the destructor!" << endl; } void Release() { delete[] data; data = nullptr; } private: unsigned char * data; }; int main() { { ImageEx imageEx; imageEx.Release(); } return 0; }
那么,當類對象離開作用域,結(jié)束生命周期之前,就必須顯示調(diào)用一次成員函數(shù)Release(),否則就會造成內(nèi)存泄漏:對象在調(diào)用析構(gòu)函數(shù)之后,只會銷毀數(shù)據(jù)成員data本身,而不是其指向的內(nèi)存。
那么,一個合理的實現(xiàn)是,將成員函數(shù)Release()放入到析構(gòu)函數(shù):
class ImageEx { public: ImageEx() { cout << "Execute the constructor!" << endl; data = new unsigned char[10]; } ~ImageEx() { Release(); cout << "Execute the destructor!" << endl; } private: unsigned char * data; void Release() { delete[] data; data = nullptr; } }; int main() { { ImageEx imageEx; } return 0; }
這樣,當類對象離開作用域,結(jié)束生命周期之前,就自動通過析構(gòu)函數(shù),實現(xiàn)了動態(tài)數(shù)組的釋放。好處是顯而易見的:實現(xiàn)了類似于內(nèi)置數(shù)據(jù)類型對象的生命周期管理,我們可以像使用內(nèi)置數(shù)據(jù)類型對象一樣使用類對象。
2.2. 不一定需要顯式析構(gòu)
在一些現(xiàn)代高級編程語言(C#、Java、Javascript)中,已經(jīng)不用去手動管理動態(tài)內(nèi)存,取而代之的,是其與操作系統(tǒng)的中間件(.net,jvm,瀏覽器)的GC(垃圾回收)機制。而在現(xiàn)代C++中,提倡通過智能指針(std::shared_ptr、std::unique_ptr、std::weak_ptr)來管理動態(tài)內(nèi)存;對于動態(tài)數(shù)組,則使用標準容器std::vector則更好。在兩者的內(nèi)部都實現(xiàn)了前文提到的對象生命周期管理,在離開作用域后,通過析構(gòu)函數(shù)自動釋放管理的內(nèi)存,無需再手動進行回收。
那么,一個顯而易見的推論就出來了,如果我們在類中使用智能指針或者vector容器來替代new/delete管理動態(tài)內(nèi)存,是不是就可以不用析構(gòu)函數(shù)了?嚴格來說,是不用顯式使用析構(gòu)函數(shù):
class ImageEx { public: ImageEx(): data(10) { cout << "Execute the constructor!" << endl; } private: std::vector<unsigned char> data; }; int main() { ImageEx imageEx; return 0; }
實際上,并不是這個類不存在析構(gòu)函數(shù),而是編譯器會為它生成一個合成的析構(gòu)函數(shù),在這個析構(gòu)函數(shù)體中,什么也不用做。因為類中的動態(tài)內(nèi)存,已經(jīng)交由std::vector容器來管理。當類對象離開作用域調(diào)用析構(gòu)函數(shù)之后,會銷毀這個std::vector容器數(shù)據(jù)成員,進而觸發(fā)其析構(gòu)函數(shù),釋放其管理的內(nèi)存。
2.3. 析構(gòu)的必要性
根據(jù)上一節(jié)內(nèi)容,不一定需要顯式析構(gòu)。因為現(xiàn)代C++的一些機制能夠幫你自動管理動態(tài)內(nèi)存。但是析構(gòu)函數(shù)還是必要的,這是由于C++語言本身的性質(zhì)決定的。作為C語言大部分內(nèi)容的超集,需要兼容C語言手動管理內(nèi)存的特性。更重要的是,現(xiàn)代操作系統(tǒng)幾乎全部由C語言編寫,與底層的交互不可避免的需要手動使用動態(tài)內(nèi)存管理。
3. 總結(jié)
所以我們就能理解了,C++這門語言的設(shè)計哲學就是就是這樣:既想要C語言的高性能,也想要高級語言高度抽象的特性。如果我們必須兼容C語言底層設(shè)計,那我們最好使用析構(gòu)函數(shù)釋放動態(tài)內(nèi)存;否則多數(shù)情況下,我們應該使用智能指針或者stl容器來管理動態(tài)內(nèi)存,從而避免顯示使用析構(gòu)函數(shù)。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C++?實現(xiàn)單鏈表創(chuàng)建、插入和刪除
這篇文章主要介紹了C++?實現(xiàn)單鏈表創(chuàng)建、插入和刪除方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07C語言二叉樹常見操作詳解【前序,中序,后序,層次遍歷及非遞歸查找,統(tǒng)計個數(shù),比較,求深度】
這篇文章主要介紹了C語言二叉樹常見操作,結(jié)合實例形式詳細分析了基于C語言的二叉樹前序,中序,后序,層次遍歷及非遞歸查找,統(tǒng)計個數(shù),比較,求深度等相關(guān)操作技巧與注意事項,需要的朋友可以參考下2018-04-04