如果代码如下:
int *p=new int (20);
delete []p;
这样时正确的我可以理解,为什么在编译器里运行如下代码同样是正确的呢?
int *p=new int (20);
delete p;//同样正确,原因呢?
…………………………………………………………………………………………………………
9.3 10:11更新
看到下面大家的解答稍微有些认识了,假如有类people
people *p=new people(20)
这个作何解释呢?
people *p=new people[20]
这个是对象数组,数组的大小为20.
可能是你筆誤,也可能是你理解有誤:int 後面的圓括號或許應該是方括號?
上面的程式碼片段,對應 p1 應該使用 delete 釋放,對應 p2 應該使用 delete[] 釋放。
如果 new 和 delete 不對應,將會發生
未定义行为
。具體到你的環境,如果你確認兩種情況都正常執行並且沒有內存洩漏的話,可以進行如下解釋:
但,這仍然是一種
未定义行为
,即不同的編譯器實作有不同的處理方式,C++ 標準沒有做出任何保證。
除了@harttle 給出的鏈接,這個鏈接中的討論同樣值得參考:http://stackoverflow.com/questions/6953457/delete-and-delete-are-the-same-when-deleting-arrays
我理解你的意思了,你覺得對數組和單一元素進行
delete
(非delete[]
)都可以透過編譯是不應該的對吧?確實不應該。在現代的程式設計語言裡沒有這樣的問題,問題出在C++的陣列指標降級。C++最初是Stroustrup為了實現物件導向機製而開始的,出於效率的考慮未設計全新的類型系統,而是基於C來擴展(C with classes)。這樣C++繼承了C的一些低層語言特性。例如:數組不可以透過參數或傳回值傳遞,傳遞的永遠是指標。至於指向的內容是陣列還是元素,維護的責任落在了開發者頭上。
陣列名在多數情況下都會降級為首元素指標,所以陣列名稱和元素指標具有相互相容的型別。如:
所以
delete
一個陣列和delete
一個指針,語法上是完全一樣的。所以編譯器不會拋出任何錯誤,但運行時會引發未定義行為。更多delete
形式的討論,請見 http://harttle.com/2015/08/07/effective-cpp-16.html2015-9-4 00:20 更新
上述的回答針對的是為什麼編譯器不給予警告或錯誤,因為陣列名稱和元素指標是型別相容的。現在來補一下「呼叫
delete
和delete[]
時到底發生了什麼事?」通常可以把
delete
關鍵字當做一個函數呼叫+記憶體回收過程,其中的那個函數呼叫你是可以重寫(override)的:delete的過程
呼叫參數指標
rawMem
所指記憶體上物件的析構函數。當然多型類別的析構函數一般是虛函數,正常的實作中會級聯地呼叫類別層級上的所有析構函數。這是編譯器完成的,在上述operator delete
之前完成。operator delete
完成預設的(或自訂的)記憶體回收,通常會去呼叫free
關鍵字。即使你沒重載operator delete
,C++也提供了一個全域的實作。delete[]的過程
得到數組的長度N。這裡不同編譯器就有不同的實作了,而且基本資料型別和物件也有差別。編譯器可能會在陣列頭儲存一個陣列長度(所以陣列大小可能大於元素大小總和,第一個元素位址也不一定等於陣列位址!)。
迭代地呼叫這N個析構函數。
operator delete
釋放N個元素的記憶體。錯誤的使用
如果用
delete
來回收數組,或用delete[]
來回收元素時,運行時行為是未定義的。就是說編譯器想怎麼實作就怎麼實現,C++標準沒有規定如何處理這種情況。不過一般而言,
delete
一個基本型別的陣列不會產生特別惡劣的影響。大不了後面的記憶體沒有回收。但delete
一個物件數組卻常常會導致程式異常退出。如:在Mac的Homebrew gcc 5.1.0上會有運行時錯誤:
總之,如果你用
new
申請了內存,就用delete
回收;如果用new []
申請了內存,就用delete[]
回收。這一點都靠自覺,編譯器不會管你。沒報錯不代表不會出錯。
簡單地說,
delete []p
會逐一執行p數組中所有物件的析構操作,而delete p
不會。更多的細節就自己搜一下吧。
這不是申請一個int空間並且初始化值為20麼。 。
你是不是想說這樣?
這樣才是申請數組
p指向的只是一個指針,至於指向單一元素還是指向數組,就得開發者自己處理了,編譯器只會在帶有[]時才當作數組的地址來釋放數組