簡單做個實驗分析一下 new/new[] 和 delete/delete[]

先來介紹下流程。

  • new/new[],他的流程是先開闢了一個空間,然後調用了構造函數。
  • delete/delete[]先調用析構函數,然後釋放內存。

那麼如果new/deletenew[]/deletep[]沒有配對會出現什麼問題。
對於這個問題我做了簡單的實驗。

非基礎類型

也就是自己寫的類。
先說下結論

  • delete釋放new[]只會調用第一個的析構函數,導致內存泄漏。
  • delete[]釋放new會出bug

測試如下

class A{
    int a;
public:
    A():a(0xffff){
        cout<<"A()\n";
    }
    ~A(){
        cout<<"~A()\n";
    }
    virtual void func(){
        cout<<"func\n";
    }
};
int main() {
    A *b=new A[4];
    delete [] b;
    
    b =new A();
    delete b;
    
    b=new A[4];
    delete b;
    
    A *a=new A();
    delete[] a;
    return 0;
}

輸出結果是

A()
A()
A()
A()
~A()
~A()
~A()
~A()//4個析構
A()
~A()//一個析構
A()
A()
A()
A()
~A()//一個析構
A()

Process finished with exit code -1073741819 (0xC0000005)//直接RE了

問題已經看出來,那麼是爲什呢。
我們去看看內存。
在這裏插入圖片描述
看到了嗎,十分明顯的一個數字,表示分配了多少個,後面很明顯吧。一個虛函數指針,和一個值。
然後執行下一行釋放內存。
在這裏插入圖片描述
看出內存的變化了嗎,全部都釋放掉了,前面那個4 也沒了。
在這裏插入圖片描述
執行到這,用new創建的明顯沒有一個表示多少個的。
執行下一行
在這裏插入圖片描述
然後沒了被釋放了。

執行後面錯誤的。
看看用delele刪除new[]
在這裏插入圖片描述

在這裏插入圖片描述
執行完後,他們全部都在,我都懷疑內存到底有沒有釋放。很顯然全部都還沒有釋放掉。

然後後面那個爲啥會RE不用我說了吧。
在這裏插入圖片描述
前面那個表述個數的數字這麼大。。你想怎麼搞。。。

小問題1不要慌

對於這個實驗,如果把虛函數刪掉,就不會是內存錯誤而是一個非常大的循環。至於爲什麼自己思考。

結論:

new參數的非基礎類型,只會開闢一個對象內存空間,new[]還會保存一個數組大小。delete[]刪除的時候會根據 刪除地址前面的4個字節表示的數字大小來刪除後面的對象。

爲了保證正確性,我來做個有意思的事,我們來手寫一個new[]

class A{
    int a;
public:
    A():a(0xffff){
        cout<<"A()\n";
    }
    ~A(){
        cout<<"~A()\n";
    }
    virtual void func(){
        cout<<"func\n";
    }
};
int main() {
    void *p=::operator new(sizeof(A)*4+sizeof(int));//這個可以用malloc來是一樣的結果。
    int *size=(int *)p;
    *size=4;//這個設置一下大小
    A *a=(A *)(size+1);//這個就是new[] 的實現
    new(a)A();
    new(a+1)A();
    new(a+2)A();
    new(a+3)A();
    delete[] a;//顯然這個是釋放
    return 0;
}

理論上delete []也能手寫實現,具體怎麼實現讀者自己動手操作,切忌拿來主義。

基本類型

對於基本類型和非基本類型有什麼區別呢???,emmmm好像沒啥區別。
但是他確實不一樣。

int main() {
    int *b=new int[4]{0xfff,0xffff,0xfff,0xfff};
    delete [] b;

    b =new int();
    delete b;

    b=new int[4]{0xfff,0xffff,0xfff,0xfff};
    delete b;

    int *a=new int();
    delete[] a;
    return 0;
}

運行這個你會發現,沒有任何問題!!!!,確實就是,沒有任何問題。
爲什麼會這樣???。
我們照着前面那個的來。
在這裏插入圖片描述
看到了嗎,沒有那個表示數組長度的東西了。
在這裏插入圖片描述
delete之後內存變了。

後面的自己看,你會發現,用delet刪除new[]完全沒問題。。。
delet[]刪除new,好像也沒啥問題。

那麼問題來了,他們是怎麼知道要釋放多少內存的。
這個就比較複雜了。他和free是一個性質,內存分配是交給操作系統完成的,具體怎麼操作的還是和內核有關。
應該也能很清楚的看見,每次釋放內存,改變的不僅僅是你需要的的內存大小,而是一整塊內存。
所以用delete/delete[]釋放內存的時候,實際上是直接把那一塊內存釋放了。

結論:

對於基礎類型,用delete/delete[]刪除實際上是一樣的,主要原因應該還是因爲,他沒有調用構造和析構的必要。如果一個類裏面封裝了一個指針,然後new了一個對象,如果不調用析構函數,就會出現內存泄漏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章