關於C++中new/delete和new[]/delete[]
阿新 • • 發佈:2019-01-06
參看連結
淺談 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
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 個位元組的地方。你應該傳入引數設為那個地址!