如果代码如下:
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.
PHP中文网2017-04-17 12:06:22
可能是你筆誤,也可能是你理解有誤:int 後面的圓括號或許應該是方括號?
int *p1 = new int(20); // 这一句是分配一个 int 的空间并将其内容初始化为 20 。
int *p2 = new int[20]; // 这一句是分配 20 个 int 的空间。
上面的程式碼片段,對應 p1 應該使用 delete 釋放,對應 p2 應該使用 delete[] 釋放。
如果 new 和 delete 不對應,將會發生未定义行为
。
具體到你的環境,如果你確認兩種情況都正常執行並且沒有內存洩漏的話,可以進行如下解釋:
因為new/delete 相對於malloc/free 差異主要在於調不呼叫建構/析構函數,
delete 和delete[] 的差異在於呼叫幾個析構函數,
而基本類型沒有構造/析構之說,所以在你的環境中兩種用法都得到了相同的結果。
但,這仍然是一種未定义行为
,即不同的編譯器實作有不同的處理方式,
C++ 標準沒有做出任何保證。
除了@harttle 給出的鏈接,這個鏈接中的討論同樣值得參考:http://stackoverflow.com/questions/6953457/delete-and-delete-are-the-same-when-deleting-arrays
高洛峰2017-04-17 12:06:22
我理解你的意思了,你覺得對數組和單一元素進行delete
(非delete[]
)都可以透過編譯是不應該的對吧?確實不應該。在現代的程式設計語言裡沒有這樣的問題,問題出在C++的陣列指標降級。
C++最初是Stroustrup為了實現物件導向機製而開始的,出於效率的考慮未設計全新的類型系統,而是基於C來擴展(C with classes)。這樣C++繼承了C的一些低層語言特性。例如:數組不可以透過參數或傳回值傳遞,傳遞的永遠是指標。至於指向的內容是陣列還是元素,維護的責任落在了開發者頭上。
陣列名在多數情況下都會降級為首元素指標,所以陣列名稱和元素指標具有相互相容的型別。如:
int arr[5];
int * p == arr; // 这是兼容的
void f1(int *);
f(arr); // 这也是兼容的
int* f2(){
int arr[2];
return arr; // 这也是兼容的
}
所以delete
一個陣列和delete
一個指針,語法上是完全一樣的。所以編譯器不會拋出任何錯誤,但運行時會引發未定義行為。更多delete
形式的討論,請見 http://harttle.com/2015/08/07/effective-cpp-16.html
上述的回答針對的是為什麼編譯器不給予警告或錯誤,因為陣列名稱和元素指標是型別相容的。現在來補一下「呼叫delete
和delete[]
時到底發生了什麼事?」
通常可以把delete
關鍵字當做一個函數呼叫+記憶體回收過程,其中的那個函數呼叫你是可以重寫(override)的:
void operator delete(void *rawMem) throw(){
// 正确地重写new/delete较为困难,可以参见Effective Item 51、52
}
呼叫參數指標rawMem
所指記憶體上物件的析構函數。當然多型類別的析構函數一般是虛函數,正常的實作中會級聯地呼叫類別層級上的所有析構函數。這是編譯器完成的,在上述operator delete
之前完成。
operator delete
完成預設的(或自訂的)記憶體回收,通常會去呼叫free
關鍵字。即使你沒重載operator delete
,C++也提供了一個全域的實作。
得到數組的長度N。這裡不同編譯器就有不同的實作了,而且基本資料型別和物件也有差別。編譯器可能會在陣列頭儲存一個陣列長度(所以陣列大小可能大於元素大小總和,第一個元素位址也不一定等於陣列位址!)。
迭代地呼叫這N個析構函數。
operator delete
釋放N個元素的記憶體。
Scott Meyers:不要假設任何C++的記憶體佈局,也不要基於該假設做任何記憶體操作。
如果用delete
來回收數組,或用delete[]
來回收元素時,運行時行為是未定義的。就是說編譯器想怎麼實作就怎麼實現,C++標準沒有規定如何處理這種情況。
不過一般而言,delete
一個基本型別的陣列不會產生特別惡劣的影響。大不了後面的記憶體沒有回收。但delete
一個物件數組卻常常會導致程式異常退出。如:
string* p = new string[4];
delete p;
在Mac的Homebrew gcc 5.1.0上會有運行時錯誤:
error for object 0x7fcd93c04b38: pointer being freed was not allocated
總之,如果你用new
申請了內存,就用delete
回收;如果用new []
申請了內存,就用delete[]
回收。這一點都靠自覺,編譯器不會管你。
PHP中文网2017-04-17 12:06:22
沒報錯不代表不會出錯。
簡單地說,delete []p
會逐一執行p數組中所有物件的析構操作,而delete p
不會。
更多的細節就自己搜一下吧。
阿神2017-04-17 12:06:22
int *p=new int (20);
這不是申請一個int空間並且初始化值為20麼。 。
你是不是想說這樣?
int *p=new int [20];
這樣才是申請數組
p指向的只是一個指針,至於指向單一元素還是指向數組,就得開發者自己處理了,編譯器只會在帶有[]時才當作數組的地址來釋放數組