2013-02-03 82 views
1

我最近發現,在我們的代碼有很多地方都在做這樣的事情:爲什麼應該要求數組刪除操作符時,常規刪除操作符是否工作?

int * int_array = new int[1000]; 

// ... do things with int_array 

delete int_array; 

當然,問題是,它應該使用delete []運營商,而不是常規的delete操作。

謎團是:這段代碼在Windows上由Visual Studio 2003和2005編譯,以及在OS X上編譯GCC/clang時,一直在用字面上的年代工作。爲什麼這不會在現在變得非常糟糕?

據我所知,我們告訴編譯器將內存釋放爲「錯誤」的方式,通常如果你這樣做,會發生一些可怕的事情,並且你的程序崩潰。爲什麼這不是爲我們發生的?現代編譯器會自動地爲你「做正確的事情」,還是足夠的正確的事情,對基本類型或其他事情無關緊要?我不能接受我們很幸運,因爲這個代碼已經被多個不同的操作系統使用了數千個客戶,這些代碼已經使用了好幾年了。

請注意,我不是要求錯誤的理由,只是想明白爲什麼我們不會因爲做錯事而麻煩。 :)

+3

未定義的行爲未定義。 – chris

+0

大部分軟件都包含很多缺陷和未定義的行爲,這些行爲並不表現爲實際的外部可觀察問題。 –

回答

5

這是未定義行爲的本質 - 它可能完全按照您的意圖執行。問題是,使用下一版本的編譯器,操作系統,庫或CPU ......它可能會做一些完全不同的事情。

最有可能的,你就要用它的原因有兩個:

  1. int沒有析構函數。所以未能正確銷燬陣列​​中的每個元素都沒有任何後果。

  2. 在此平臺上,newnew[]使用相同的分配器。所以你不會將一個塊返回給錯誤的分​​配器。

+0

這是現貨。查看了[gcc的libstdC++]中全局新/刪除的源代碼(https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/del_opv.cc )和[clang的libC++](http://llvm.org/svn/llvm-project/libcxx/trunk/src/new.cpp),似乎在這些實現中'delete []'簡單地調用'delete'。 –

1

這是行不通的。刪除沒有[]的陣列會泄漏內存。如果您的應用程序運行很長時間,它會影響性能。對於短期計劃,不存在問題。

另一件需要注意的是delete將銷燬所有由new分配的內存。如果您通過new(而不是new [])分配了數組,則可以使用delete來銷燬它。

+0

根據編譯器的不同,它甚至可能會崩潰:http://ideone.com/kuAI44 – bikeshedder

+0

@bikeshedder請參閱第二段。 –

1

這不會導致程序崩潰,但每當你這樣做時,你將會泄漏999 * sizeof(int)字節的內存。

+0

根據編譯器的不同,它甚至可能會崩潰:http://ideone.com/kuAI44 – bikeshedder

+0

爲什麼不是'1000 * sizeof(int)'? – 0x499602D2

+0

我會假設這是因爲你破壞了數組中的第一個元素。但不是其餘。 – villadelfia

1

根據分配器的不同,常規的delete運算符可能實際上釋放了所有內存,但真正的問題是它不會在數組元素上運行任何析構函數。

3

讓我們通過什麼情況,一步步運行(但我們會忽略例外):

int *foo = new int[100]; 

這種分配100*sizeof(int)字節的內存,並呼籲int::int() 100次,一次在每個元素陣列。

由於int是內建類型,它有一個微不足道的構造函數(即它什麼都不做)。

現在,如何:

delete foo; 

這將調用int::~int()上的地址指向foo,然後刪除內存foo指向。再一次,因爲int是一個內置類型,它有一個微不足道的析構函數。

比較這對:

delete [] foo; 

它將調用int::~int()每個在陣列指向foo的項目,然後刪除該存儲器通過FOO指向。

這裏的基本區別是元素1..99不會被破壞。對於int這不是問題,這可能是您現有代碼工作的原因。對於具有真正析構函數的對象數組,您會看到更多的錯誤行爲。

P.S.如果您編寫delete foo;(即使您使用array-new進行分配),大多數實現都將刪除foo指向的整個內存塊 - 但是如果您指望這樣做會很愚蠢。

+0

我以前沒有看到這個答案。這是迄今爲止最清楚的解釋。 –