搜尋

首頁  >  問答  >  主體

C++ new delete操作

如果代码如下:
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.

黄舟黄舟2768 天前543

全部回覆(4)我來回復

  • PHP中文网

    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

    回覆
    0
  • 高洛峰

    高洛峰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

    2015-9-4 00:20 更新

    上述的回答針對的是為什麼編譯器不給予警告或錯誤,因為陣列名稱和元素指標是型別相容的。現在來補一下「呼叫deletedelete[]時到底發生了什麼事?」

    通常可以把delete關鍵字當做一個函數呼叫+記憶體回收過程,其中的那個函數呼叫你是可以重寫(override)的:

    void operator delete(void *rawMem) throw(){
        // 正确地重写new/delete较为困难,可以参见Effective Item 51、52
    }

    delete的過程

    1. 呼叫參數指標rawMem所指記憶體上物件的析構函數。當然多型類別的析構函數一般是虛函數,正常的實作中會級聯地呼叫類別層級上的所有析構函數。這是編譯器完成的,在上述operator delete之前完成。

    2. operator delete完成預設的(或自訂的)記憶體回收,通常會去呼叫free關鍵字。即使你沒重載operator delete,C++也提供了一個全域的實作。

    delete[]的過程

    1. 得到數組的長度N。這裡不同編譯器就有不同的實作了,而且基本資料型別和物件也有差別。編譯器可能會在陣列頭儲存一個陣列長度(所以陣列大小可能大於元素大小總和,第一個元素位址也不一定等於陣列位址!)。

    2. 迭代地呼叫這N個析構函數。

    3. 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[]回收。這一點都靠自覺,編譯器不會管你。

    回覆
    0
  • PHP中文网

    PHP中文网2017-04-17 12:06:22

    沒報錯不代表不會出錯。

    簡單地說,delete []p 會逐一執行p數組中所有物件的析構操作,而delete p不會。

    更多的細節就自己搜一下吧。

    回覆
    0
  • 阿神

    阿神2017-04-17 12:06:22

    int *p=new int (20);

    這不是申請一個int空間並且初始化值為20麼。 。

    你是不是想說這樣?

    int *p=new int [20];

    這樣才是申請數組

    p指向的只是一個指針,至於指向單一元素還是指向數組,就得開發者自己處理了,編譯器只會在帶有[]時才當作數組的地址來釋放數組

    回覆
    0
  • 取消回覆