你知道C++中new和delete為什么要匹配使用嗎
前言
關(guān)于 new 和 delete 的使用相信大家并不陌生,可是為什么使用 new 的時(shí)候要用 delete,使用 new[] 的時(shí)候又要用 delete[]。使用 delete 釋放 new[] 申請(qǐng)的內(nèi)存又會(huì)發(fā)生什么?為什么有時(shí)不匹配不會(huì)報(bào)錯(cuò),有時(shí)又會(huì)報(bào)錯(cuò)呢?本文將為大家解決這個(gè)疑惑。
string* strArray = new string[1002]; ... delete strArray;
這段代碼看起來(lái)似乎沒(méi)有問(wèn)題,既使用了 new,又搭配了 delete。但這代碼的行為是未定義的,最好的情況是有部分?jǐn)?shù)據(jù)沒(méi)有析構(gòu),但更可能的是程序直接崩潰。
new 和 delete 做了什么
當(dāng)使用 new 動(dòng)態(tài)生成對(duì)象時(shí),有兩件事發(fā)生:
1.調(diào)用全局的 operator new 分配內(nèi)存
2.調(diào)用該對(duì)象的構(gòu)造函數(shù),對(duì)分配的內(nèi)存進(jìn)行初始化
內(nèi)置類型的構(gòu)造函數(shù)在這時(shí)什么都不做,故編譯器不用調(diào)用
當(dāng)使用 delete 時(shí),同樣會(huì)發(fā)生兩件事:
1.針對(duì)這段內(nèi)存會(huì)有一個(gè)(或多個(gè))析構(gòu)函數(shù)被調(diào)用
2.調(diào)用全局的 operator delete 釋放內(nèi)存
delete 的問(wèn)題在于:將要釋放的內(nèi)存中有多少個(gè)對(duì)象?這將決定有多少個(gè)析構(gòu)函數(shù)被調(diào)用。
這個(gè)問(wèn)題可以轉(zhuǎn)化為:指針?biāo)赶虻氖菃我粚?duì)象還是對(duì)象數(shù)組?如果是對(duì)象數(shù)組,那么里面又有多少個(gè)對(duì)象?因此我們有必要記錄數(shù)組的大小,以便 delete 知道需要調(diào)用多少次析構(gòu)函數(shù)。例如下圖:
當(dāng)然這只是個(gè)例子,不同的編譯器可能會(huì)有不同的方案,此處我們假定 n 占用 4 字節(jié)。
內(nèi)置類型
new 和 new []
int* num = new int; int* arr = new int[1002];
因?yàn)閮?nèi)置類型的默認(rèn)構(gòu)造函數(shù)并不會(huì)做什么處理,所以上述代碼在調(diào)用完 ::operator new() / ::operator new[]() 后,不會(huì)調(diào)用構(gòu)造函數(shù)。同樣,因?yàn)闊o(wú)需調(diào)用構(gòu)造函數(shù),也不用多開(kāi)辟一塊空間存儲(chǔ)對(duì)象個(gè)數(shù)。
delete 和 delete [ ]
int* num = new int; int* arr = new int[1002]; delete num; delete[] arr;
對(duì)于內(nèi)置類型的 delete 也是非常簡(jiǎn)單,只是單純的調(diào)用 ::operator delete() / ::operator delete[]() 釋放內(nèi)存。內(nèi)置類型沒(méi)有開(kāi)辟存儲(chǔ)對(duì)象個(gè)數(shù)的空間,也不需要析構(gòu)函數(shù)處理。
自定義類型
new 和 new []
string* str1 = new string; string* str2 = new string[1231];
上述代碼首先會(huì)調(diào)用 ::operator new() / ::operator new[]() 申請(qǐng)內(nèi)存,然后再調(diào)用 string 的默認(rèn)構(gòu)造函數(shù)進(jìn)行初始化工作。
對(duì)于 new:因?yàn)橹挥幸粋€(gè)對(duì)象,最后直接構(gòu)造該對(duì)象即可,不用記錄個(gè)數(shù)。
對(duì)于 new []:因?yàn)樽詈笮枰{(diào)用析構(gòu)函數(shù)析構(gòu)所有對(duì)象,所以需要記錄對(duì)象的個(gè)數(shù)(即使是使用 new [] 申請(qǐng)一個(gè)對(duì)象),于是就在內(nèi)存開(kāi)頭處 4 字節(jié)記錄對(duì)象的個(gè)數(shù)(一種可能的方案),然后返回實(shí)際開(kāi)辟的內(nèi)存 + 4 的位置。
delete 和 delete [ ]
string* str1 = new string; string* str2 = new string[1231]; delete str1; delete[] str2;
上述代碼首先會(huì)調(diào)用 string 的析構(gòu)函數(shù)進(jìn)行清理工作,然后再調(diào)用 ::operator delete() / ::operator delete[]() 釋放內(nèi)存。
對(duì)于 delete:直接析構(gòu)該位置對(duì)象,然后釋放即可。
對(duì)于 delete[]:首先會(huì)查看前面 4 字節(jié)存儲(chǔ)的個(gè)數(shù),來(lái)決定調(diào)用析構(gòu)函數(shù)的次數(shù);然后再調(diào)用 ::operator delete[]() 釋放內(nèi)存,實(shí)際上 ::operator delete[]() 釋放的是地址減 4 后的值。因?yàn)閷?shí)際開(kāi)辟的前面還有 4 個(gè)字節(jié),要是不修正的話直接釋放的話,程序就會(huì)崩潰,因?yàn)橄到y(tǒng)并沒(méi)記錄該地址。
問(wèn)題
自定義類型為什么要匹配使用
1.使用 new 和 delete
開(kāi)辟一個(gè)對(duì)象,釋放一個(gè)對(duì)象,沒(méi)有問(wèn)題
2.使用 new[] 和 delete
開(kāi)辟一組對(duì)象,調(diào)用 ::operator delete(),因?yàn)闆](méi)有對(duì)地址進(jìn)行修正,釋放了非法地址,程序會(huì)崩潰
3.使用 new 和 delete[]
開(kāi)始一個(gè)對(duì)象,調(diào)用 ::operator delete[](),此時(shí)仍會(huì)把前面 4 個(gè)字節(jié)當(dāng)成個(gè)數(shù)調(diào)用析構(gòu)函數(shù)(個(gè)數(shù)是不確定的),然后再釋放減 4 后的地址,同樣釋放了非法地址,導(dǎo)致程序崩潰
4.使用 new[] 和 delete[]
開(kāi)辟一組對(duì)象,釋放一組對(duì)象,沒(méi)有問(wèn)題
內(nèi)置類型不匹配為什么不報(bào)錯(cuò)
使用 new[] 和 delete 或者 使用 new 和 delete[]
因?yàn)閷?duì)于內(nèi)置類型,默認(rèn)情況下不會(huì)調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù),也不用開(kāi)辟空間存儲(chǔ)對(duì)象個(gè)數(shù)。
因此最后的地址也不需要修正,于是就不會(huì)發(fā)生報(bào)錯(cuò)了。
疑惑
其實(shí)全局的 new 和 delete 最后調(diào)用的是 malloc 和 free,malloc 在開(kāi)辟空間時(shí),同樣會(huì)多開(kāi)辟一塊空間用于存儲(chǔ)內(nèi)存塊的信息(在 Linux 64 位環(huán)境下是 16 字節(jié))。
頭信息中有存儲(chǔ)內(nèi)存的字節(jié)大小,所以 free 時(shí)可以知道要釋放的內(nèi)存大小。
那么問(wèn)題來(lái)了,如果說(shuō)對(duì)于自定義類型 new[] 返回的地址是上圖 malloc 返回的地址,對(duì)于該地址調(diào)用全局 delete,再傳遞給 free 此時(shí)是可以正確釋放內(nèi)存的,只是析構(gòu)函數(shù)調(diào)用次數(shù)不正確。但實(shí)際上程序直接崩潰,也就是說(shuō)此時(shí) new[] 返回的地址應(yīng)該是 malloc 返回的地址右移 4 字節(jié)的值。
對(duì)這段內(nèi)存模型只是猜測(cè),博主也是菜雞,希望有大佬可以指正一下。
到此這篇關(guān)于你知道C++中new和delete為什么要匹配使用嗎的文章就介紹到這了,更多相關(guān)C++ new delete內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C++中十六進(jìn)制字符串轉(zhuǎn)數(shù)字(數(shù)值)
這篇文章主要介紹了詳解C++中十六進(jìn)制字符串轉(zhuǎn)數(shù)字(數(shù)值)的相關(guān)資料,這里提供兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2017-08-08C語(yǔ)言鏈表實(shí)現(xiàn)簡(jiǎn)單圖書(shū)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言鏈表實(shí)現(xiàn)簡(jiǎn)單圖書(shū)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03C語(yǔ)言實(shí)現(xiàn)opencv提取直線、輪廓及ROI實(shí)例詳解
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)opencv提取直線、輪廓及ROI實(shí)例詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01C++結(jié)構(gòu)體struct和類class區(qū)別詳解
struct和class有什么區(qū)別?最本質(zhì)的一個(gè)區(qū)別就是默認(rèn)的訪問(wèn)控制:默認(rèn)的繼承訪問(wèn)權(quán)限,struct是public的,class是private的。2017-11-11Qt數(shù)據(jù)庫(kù)應(yīng)用之實(shí)現(xiàn)通用數(shù)據(jù)生成器
有兩種應(yīng)用場(chǎng)景需要用到數(shù)據(jù)生成器,一種是需要測(cè)試數(shù)據(jù)庫(kù)性能,一種是隨機(jī)模擬生成一堆數(shù)據(jù),用來(lái)測(cè)試程序的性能。本文將利用Qt實(shí)現(xiàn)通用數(shù)據(jù)生成器,需要的可以參考一下2022-02-02C++ 動(dòng)態(tài)創(chuàng)建按鈕及 按鈕的消息響應(yīng)
這篇文章主要介紹了C++ 動(dòng)態(tài)創(chuàng)建按鈕及 按鈕的消息響應(yīng)的相關(guān)資料,需要的朋友可以參考下2015-06-06簡(jiǎn)單說(shuō)說(shuō)STL的內(nèi)存管理
<STL 源碼剖析>將其描述為空間配置器,理由是allocator可以將其它存儲(chǔ)介質(zhì)(例如硬盤(pán))做為stl 容器的存儲(chǔ)空間。由于內(nèi)存是allocator管理的主要部分,因此,本文以STL內(nèi)存管理為出發(fā)點(diǎn)介紹allocator2013-09-09