2008-10-31 48 views
23

這是一個嘮叨我一段時間的問題。我一直認爲應該設計C++,以便即使使用「new []」運算符,「刪除」運算符(無括號)也能工作。爲什麼我們甚至需要「delete []」運算符?

在我看來,寫這個:

int* p = new int; 

應相當於分配1個元素的數組:

int* p = new int[1]; 

如果這是真的,「刪除」操作者總是可以刪除數組,而我們不需要「delete []」運算符。

爲什麼在C++中引入「delete []」運算符有什麼原因嗎?我能想到的唯一原因是分配數組的內存佔用量很小(您必須將數組大小存儲在某處),因此區分「delete」和「delete []」是一個小內存優化。

回答

32

這樣就可以調用各個元素的析構函數。是的,對於POD數組,沒有什麼區別,但在C++中,可以使用非平凡的析構函數來創建對象數組。

現在,你的問題是,爲什麼不把newdelete的行爲很像0​​和delete[],擺脫new[]delete[]?我會回到Stroustrup的「設計和演變」一書中,他說如果你不使用C++特性,你不應該爲它們付費(至少在運行時)。現在,newdelete的行爲將與mallocfree一樣有效。如果delete具有delete[]的含義,那麼在運行時會有一些額外的開銷(正如James Curran指出的那樣)。

+4

實際上,當你使用new int [1]時,它在第一個數據之前存儲在數組的頭部,它的大小。因此,使用delete而不是delete []不會釋放該部分內存。 – Rodrigo 2009-04-24 13:35:03

2

delete []確保每個成員的析構函數被調用(如果適用於該類型),而delete只是刪除爲陣列分配的內存。

這裏有一個很好看的:http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=287

沒有,數組大小是不存儲在C++。(謝謝大家指出這個說法是不準確的。)

+0

不同意你最後的陳述。編譯器必須知道數組大小才能調用數組中每個對象的析構函數。我認爲你把這一點與C++不對數組進行邊界檢查的事實混淆起來。 – 2008-10-31 03:39:19

+0

哦,真的。我以爲你在暗示大小將作爲數組數據結構(緩衝區)的一部分進行存儲。是的,編譯器可能需要在某處存儲大小信息... – 2008-10-31 04:07:28

+0

一種方法是在數組開始之前在單詞中存儲元素的大小和數量。這被稱爲cookie。 – Dynite 2008-10-31 09:16:01

6

由於其他人似乎都錯過了你的問題的重點,我只是補充說,我在一年前有同樣的想法,並且從來沒有能夠得到答案。

我能想到的唯一的事情是,有額外的開銷非常點點對待單個對象作爲數組(不必要的「for(int i=0; i<1; ++i)」)

8

媽的,我錯過了問題的整點,但我將留下原始答案作爲旁註。爲什麼我們刪除[]是因爲很久以前我們刪除了[cnt],即使在今天,如果您編寫delete [9]或delete [cnt],編譯器也會忽略[]之間的內容,但編譯成功。那時,C++首先被前端處理,然後被送到普通的C編譯器。他們無法做到在帳幕下方存放帳號的伎倆,也許他們當時甚至想不起來。而爲了向後兼容,編譯器很可能使用[]中給出的值作爲數組的計數,如果沒有這樣的值,那麼他們從前綴中獲得計數,所以它可以兩種方式工作。稍後,我們在[]和所有工作之間都沒有輸入任何內容。今天,我不認爲「刪除[]」是必要的,但是實現需要這種方式。

我原來的答覆(即忽略了一點)::

「刪除」刪除單個對象。 「delete []」刪除一個對象數組。爲了使delete []起作用,實現保留了數組中元素的數量。我只是通過調試ASM代碼來檢查這一點。在我測試的實現(VS2005)中,計數被存儲爲對象數組的前綴。

如果您在單個對象上使用「delete []」,則count變量是垃圾,因此代碼會崩潰。如果您使用「刪除」對象數組,由於一些不一致,代碼崩潰。我剛纔測試了這些案例!

「刪除只是刪除分配給陣列的內存」。在另一個答案中的陳述是不正確的。如果對象是一個類,delete將調用DTOR。只要在DTOR代碼中放置一個斷點並刪除該對象,斷點就會命中。

我想到的是,如果編譯器&庫假定所有由「new」分配的對象都是對象數組,那麼對單個對象或對象數組調用「delete」是可以的。單個對象僅僅是具有1計數對象數組的特殊情況,也許有我丟失的東西,反正...

5

因爲沒有其他答案目前解決它添加此:

陣列delete[]不能在指針到基類的類上使用 - 當編譯器在調用new[]時存儲對象的數量時,它不會存儲對象的類型或大小(如David所指出的那樣,在C++中,您很少支付對於你沒有使用的功能)。但是,標量delete可以通過基類安全地刪除,所以它既用於正常對象清理和多形清理:

struct Base { virtual ~Base(); }; 
struct Derived : Base { }; 
int main(){ 
    Base* b = new Derived; 
    delete b; // this is good 

    Base* b = new Derived[2]; 
    delete[] b; // bad! undefined behavior 
} 

然而,在相反的情況下 - 非虛析 - 標量delete應儘可能廉價儘可能 - 它不應該檢查對象的數量,也不要檢查被刪除的對象的類型。這使得刪除內置型或純老的數據類型很便宜,因爲編譯器只需要調用::operator delete沒有別的:

int main(){ 
    int * p = new int; 
    delete p; // cheap operation, no dynamic dispatch, no conditional branching 
} 

雖然不是一個詳盡的治療內存分配的,我希望這有助於澄清在C++中可用的內存管理選項的廣度。

0

我對Aaron的回答有些困惑,坦率地承認我並不完全理解爲什麼和在哪裏需要刪除[]。

我用他的示例代碼做了一些實驗(修復了幾個拼寫錯誤之後)。這是我的結果。 錯別字:〜基地需要一個函數體 基地* B被宣佈兩次

struct Base { virtual ~Base(){ }>; }; 
struct Derived : Base { }; 
int main(){ 
Base* b = new Derived; 
delete b; // this is good 

<strike>Base</strike> b = new Derived[2]; 
delete[] b; // bad! undefined behavior 
} 

編譯和執行

[email protected]:g++ -o atest atest.cpp 
[email protected]: ./atest 
[email protected]: # No error message 

與刪除[]除去

struct Base { virtual ~Base(){}; }; 
struct Derived : Base { }; 

int main(){ 
    Base* b = new Derived; 
    delete b; // this is good 

    b = new Derived[2]; 
    delete b; // bad! undefined behavior 
} 

編譯和執行修改的程序

[email protected]:g++ -o atest atest.cpp 
[email protected]: ./atest 
atest(30746) malloc: *** error for object 0x1099008c8: pointer being freed was n 
ot allocated 
*** set a breakpoint in malloc_error_break to debug 
Abort trap: 6 

當然,我不知道delete [] b實際上是否在第一個例子中工作;我只知道它沒有給出編譯器錯誤信息。

相關問題