C++中為什麼有delete[]這種寫法?
為什麼delete不能識別出數組內存分配呢?
我知道是某些【歷史原因】,但是新的標準和編譯器擴展為什麼不能實現這個簡單的功能呢?
&> 新的標準和編譯器擴展為什麼不能實現這個簡單的功能呢?
因為違反了 zero overhead 原則。實現這個功能不複雜,無非是把對象的new視為長度為1的數組的new,即:
String* p1 = new String;
編譯器改寫為:String* p2 = new String[1];然後統一用 delete[] 來釋放(把delete也看成delete[])。只不過這麼做對於non-POD類型需要一個額外的長度來保存對象的個數,因此增加了開銷,違反了原則。通常要比sizeof(String) 多分配sizeof(size_t)來保存這個「數」。
發散一下,假如下一版的編譯器/標準真的這麼做了,就會有人為了省這麼幾個位元組,用 malloc(sizeof(String)) + placement new 來退回原來(現在)的行為,並且把這個「技巧」寫進下一版的 Effective C++ 或 Efficient C++。
另外 Base* p = new Derived[10]; 就算用 delete[] p; 也不能正確釋放,除非編譯器做了擴展。以 VC++ 2008 舉例,如果執行以下代碼
Foo *p = new Foo[10];
int n = *((int*)p - 1);
猜猜 n 的值是多少?是 10
編譯器在使用new[]分配內存的時候在返回的內存塊里放了一個 數組的 size
而用 new 分配的時候則不會在使用 delete 的時候,生成的代碼最後是用 p 去調用free
而是用 delete[]的時候,生成的代碼是 用 p - 4(按位元組減) 去調用free但是編譯器無法分辨給出的 *p 到底是指向數組還是指向單個對象,所以需要開發者自己指定。那麼如果用 delete作用在一個 new[]分配的指針上會怎麼樣呢?debug版下會有assert,release版下會造成內存泄漏,因為調用free時指針非法會失敗,但是不會崩潰。delete數組和delete元素想用一個操作符來實現,這個行為本身就是想要多態,沒虛函數根本不可能支持0額外開銷.....
如果是虛析構方法,並且禁止各自析構(比方說你先把第3個元素手動delete了,然後最後又去delete第一個元素.其實傳統C++編程應該也不允許這樣玩吧?),解決的方法有一個:
1.編譯的時候,第一次遇到一次new T[]就產生一個虛表的拷貝t。這個虛表顯然除了析構函數外其它虛函數地址都不會變,而新析構函數實現為按數組析構。
2.鏈接的時候,合併所有的同類型T的虛表t。
3.執行new[]的時候,把第一個元素的虛表指針指向這個新的虛表t,再把數組長度拷貝到某個預先設定的地址x(編譯的時候必須生成這些動作的代碼)。第二個動作是已有編譯器的實現啦。
這樣就可以痛快的delete第一個元素了。 從編譯器實現的角度來說,就是做了一次繼承,產生了一種新類型,修改了虛析構函數的實現。代價就是產生了一個新的虛表的開銷。
不過既然非虛析構對象都不能支持,單獨支持這個也沒太多意義。因為面向對象的, 所以很麻煩.如果delete既能釋放一個元素, 又能釋放一個數組, 會造成混淆, 還會在申請釋放時做額外的判斷, 浪費額外的內存. 那樣的話不如乾脆不提供new []了.自己創建數組存儲.CT* t[10] = {0};for(int i=0; i&<10; i++){
t[i] = new CT();
}原文鏈接:C++中delete和delete[]的區別
====================================================
請看下面的程序。#include &
using namespace std;
class T {
public:
T() { cout &<&< "constructor" &<&< endl; }
~T() { cout &<&< "destructor" &<&< endl; }
};
int main()
{
const int NUM = 3;
T* p1 = new T[NUM];
cout &<&< hex &<&< p1 &<&< endl;
delete p1;
T* p2 = new T[NUM];
cout &<&< p2 &<&< endl;
delete[] p2;
}
大家可以自己運行這個程序,看一看 delete p1 和 delete[] p1 的不同結果,我就不在這裡貼運行結果了。
從運行結果中我們可以看出,delete p1 在回收空間的過程中,只有 p1[0] 這個對象調用了析構函數,其它對象如 p1[1]、
p1[2] 等都沒有調用自身的析構函數,這就是問題的癥結所在。如果用 delete[],則在回收空間之前所有對象都會首先調用自己的析構函數。
基本類型的對象沒有析構函數,所以回收基本類型組成的數組空間用 delete 和 delete[] 都是應該可以的;但是對於類對象數組,只能
用 delete[]。對於 new 的單個對象,只能用 delete 不能用 delete[] 回收空間。
萬一是只delete這個數組中的某一個呢?是不是應該得分開設計?
推薦閱讀:
※關於指針數組的初始化的一個問題?
※C++ string + 號返回的是右值,為什麼下面這段代碼不報錯?
※為什麼C++的庫函數的定義會這麼複雜?
※如何才能寫出沒有bug的程序?
※為什麼OJ上有些人提交的代碼運行那麼快? 有人總結這些技巧么