delete與delete[]在此版中已是老生常談的問題了,隔三差五就要冒出來一次, 樓上幾位也已似說得很清楚,本不應再囉嗦.不過俺覺得這回樓主的問題頗有新意(此段代碼似是高人所爲,呵呵),不禁想湊熱鬧聊幾句.
首先,delete與delete[]混用的後果不只是樓上幾位說的內存泄漏(否則如何解釋runtime error). 事實上, C++標準5.3.5/2說得很清楚:
...In the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a sub-object (1.8) representing a base class of such an object (clause 10). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression.72) If not, the behavior is undefined.
注意後果是很恐怖的"undefined". 如果你混用, 誰也不能保證會是什麼結果(與對象類型有關,更重要的,與編譯器有關). 幸運的話,一切正常, 稍不幸的,內存泄漏. 最不幸的情況給你個runtime error導致程序終止也不能怨它.
在標準C++的範疇就只能說到此. 實際工作中知道這些也完全夠用了. 如果象樓主要細究runtime error的原因,就不能不涉及與編譯器有關的內容. 以下討論侷限於VC debug模式(VC6.0, VS2003, VS2005). 我猜測樓主的編譯環境也是VC,不知對否.
在VC中,對於有顯式析構函數的對象, 在分配數組時其前會有一個4字節前綴用來保存數組元素個數. 如果用delete來釋放數組, 就會導致釋放的內存地址與分配時的內存地址出現4字節偏差, 而導致程序掛掉. 詳情可參考 http://topic.csdn.net/u/20070712/07/57c7cfc6-7314-400d-86d2-230a72581ea5.html 此程序runtime error的原因就在於此4字節前綴.
先看第一個runtime error的地方 CTest* t =(CTest*) new CTest2[2]; delete [] t; 先表明一下態度,呵呵.在實際中把CTest2數組cast成一個毫無關係的CTest數組是毫無意義且非常危險的.
再分析一下這樣作有沒有什麼直接後果.注意CTest和CTest2都是有(顯式)析構函數的.析構函數體是空的,因此調用析構函數時(即使對象指針是錯的)倒不會出錯.
因爲CTest2有析構函數,故爲數組t申請的內存開始地址是&t[0]前4字節. 而在delete []t時,編譯器以爲t是CTest數組,它注意到CTest有析構函數,故釋放內存時調用free(p)的參數值也是&t[0]前4字節,很幸運,通過了.
而如果把delete []t換成delete t,編譯器不再理會那4字節前綴,而直接用&t[0]的值調用free(p). 因此,釋放的內存指針與申請時相差4字節! 在VC debug版中,爲了便於用戶發現bug,凡是以錯誤地址調用free(p)的都會觸發Assertion.因此,runtime error發生了.
第二個runtime error也類似: CTest* t =(CTest*) new int[2]; delete t; int型是沒有析構函數的.因此數組t申請的內存開始地址就是&t[0]. delete t直接以&t[0]爲參數值調用free(p), 正常通過.
而如果把delete t換成delete []t, 編譯器看到CTest是有析購函數的,因此它以爲數組t申請的內存開始地址是&t[0]前4字節,而實際上不是這樣.因此,又發生runtime error了.
以一個小實驗結束討論. 不增減[],而註釋掉CTest的析構函數,也一樣導致runtime error. 有興趣的可以試試. |