1. 程式人生 > >關於C++中new/delete和new[]/delete[]

關於C++中new/delete和new[]/delete[]

參看連結
淺談 C++ 中的 new/delete 和 new[]/delete[]

operator new 和 operator delete

這兩個其實是 C++ 語言標準庫的庫函式,原型分別如下:

void *operator new(size_t);     //allocate an object
void *operator delete(void *);    //free an object

void *operator new[](size_t);     //allocate an array
void *operator delete[](void *);    //free an array

new 和 delete 背後機制

class A
{
public:
    A(int v) : var(v)
    {
        fopen_s(&file, "test", "r");
    }
    ~A()
    {
        fclose(file);
    }

private:
    int var;
    FILE *file;
};
class A *pA = new A(10);

來建立一個類的物件,返回其指標pA。如下圖所示 new背後完成的工作:

總結一下:

  • 首先需要呼叫上面提到的 operator new 標準庫函式,傳入的引數為 class A
    的大小,這裡為 8 個位元組,至於為什麼是 8 個位元組,你可以看看《深入 C++ 物件模型》一書,這裡不做多解釋。這樣函式返回的是分配記憶體的起始地址,這裡假設是0x007da290
    上面分配的記憶體是未初始化的,也是未型別化的,
  • 第二步就在這一塊原始的記憶體上對類物件進行初始化,呼叫的是相應的建構函式,這裡是呼叫 A:A(10); 這個函式,從圖中也可以看到對這塊申請的記憶體進行了初始化,var=10, `file 指向開啟的檔案。
  • 最後一步就是返回新分配並構造好的物件的指標,這裡 pA 就指向 0x007da290 這塊記憶體,pA的型別為類 A 物件的指標。
delete pA;

delete 所做的事情如下圖所示:

delete就做了兩件事情:

  • 呼叫 pA 指向物件的解構函式,對開啟的檔案進行關閉。
  • 通過上面提到的標準庫函式operator delete 來釋放該物件的記憶體,傳入函式的引數為pA的值,也就是 0x007d290

如何申請和釋放一個數組?

new [] 一個物件陣列時,需要儲存陣列的維度,C++ 的做法是在分配陣列空間時多分配了 4 個位元組 的大小,專門儲存陣列的大小,在 delete []時就可以取出這個儲存的數,就知道了需要呼叫解構函式多少次了。

呼叫

class A *pAa = new A[3];

時需要做的事情如下:

delete []pAa;

這裡要注意的兩點是

  • 呼叫解構函式的次數是從陣列物件指標前面的 4 個位元組中取出;
  • 傳入 operator delete[] 函式的引數不是陣列物件的指標 pAa,而是 pAa 的值減 4。

為什麼 new/delete 、new []/delete[] 要配對使用?

如果是帶有自定義解構函式的類型別,用 new [] 來建立類物件陣列,而用 delete來釋放會發生什麼?用上面的例子來說明:

class A *pAa = new class A[3];
delete pAa;

那麼 delete pAa;做了兩件事:

  • 呼叫一次 pAa指向的物件的解構函式;
  • 呼叫 operator delete(pAa);釋放記憶體。

顯然,這裡只對陣列的第一個類物件呼叫了解構函式,後面的兩個物件均沒呼叫解構函式,如果類物件中申請了大量的記憶體需要在解構函式中釋放,而你卻在銷燬陣列物件時少呼叫了解構函式,這會造成記憶體洩漏。

上面的問題你如果說沒關係的話,那麼第二點就是致命的了!直接釋放pAa指向的記憶體空間,這個總是會造成嚴重的段錯誤,程式必然會奔潰!因為分配的空間的起始地址是 pAa 指向的地方減去 4 個位元組的地方。你應該傳入引數設為那個地址!