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[] 回收空間。

所以一個簡單的使用原則就是:new 和 delete、new[] 和 delete[] 對應使用。


萬一是只delete這個數組中的某一個呢?是不是應該得分開設計?


推薦閱讀:

關於指針數組的初始化的一個問題?
C++ string + 號返回的是右值,為什麼下面這段代碼不報錯?
為什麼C++的庫函數的定義會這麼複雜?
如何才能寫出沒有bug的程序?
為什麼OJ上有些人提交的代碼運行那麼快? 有人總結這些技巧么

TAG:編程 | C | CC | 編譯器 |