一、new/delete 簡介
C 語言中的動態內存管理方式有malloc、calloc、realloc 和 free,但是 malloc 和 free 是函數,new 和 delete 是 C++ 用於管理堆內存的兩個運算符
二、new/delete實現原理及操作
1.內置類型操作
void Test1()
{
int* p1 = new int;//動態申請1個int類型的空間
int* p2 = new int(10);//動態申請1個int類型的空間,並初始化爲10
int* p3 = new int[10];//動態申請10個int類型的空間(類型爲int[10])
int* p4 = new int[10]{ 1,2,3,4,5,6,7,8,9,0 };//動態申請10個int類型的空間。並初始化爲{1~0}
delete p1;
delete p2;
delete[] p3;
delete[] p4;
}
- 申請內置類型空間時,new和malloc,delete和free基本類似。
- 申請和釋放單個內存,用new和delete操作符,申請和釋放連續的空間,用new[]和delete[]操作符。
- new在申請失敗時會拋出異常,malloc會返回NULL
如果new和delete沒有匹配使用,產生後果?
void Test2()
{
int* p1 = new int;
int* p2 = new int[10];
int* p3 = new int[10];
delete[] p1;
delete p2;
free(p3);
//...
}
結論:如果申請的是內置類型的空間,不會產生任何後果
2.自定義類型操作
class Test
{
public:
Test()
:_data(10)
,_p(new int)
{
cout << "Test():" << this << endl;
}
~Test()
{
delete _p;
cout << "~Test():" << this << endl;
}
private:
int _data;
int* _p;
};
void Test3()
{
Test* p1 = new Test;//申請單個Test類型的對象
delete p1;
cout << endl;
Test* p2 = new Test[10];//申請10個Test類型的對象
delete[] p2;
Test* p3 = (Test*)malloc(sizeof(Test)); //申請單個Test類型的空間
//malloc申請空間時不會調用構造函數--申請的是與對象大小相同的一塊內存空間,不能將該塊內存空間看成一個對象
free(p3);
Test* p4 = (Test*)malloc(sizeof(Test) * 10);//申請10個Test類型的空間
free(p4);
}
new的原理
- 調用operator new函數申請空間,operator new函數實際通過malloc來申請空間,空間申請成功直接返回,申請失敗時檢測是否有空間不足應對措施,有則繼續申請,沒有就拋出異常。
- 在申請的空間上調用構造函數,完成對象的構造
delete的原理
- 在空間上調用析構函數,完成對象中資源的清理工作
- 調用operator delete函數釋放對象的空間,operator delete函數本質是通過free來釋放空間的。
new T[N]的原理
- 調用operator new[]函數,在operator new[]中實際調用operator new函數,而operator new函數實際通過malloc循環申請空間,完成N個對象空間的申請。
- 在申請的空間上調用N次構造函數。
delete[ ]的原理
- 在釋放的對象空間上調用N次析構函數,完成N個對象中資源的清理。
- 調用==operator delete[]==釋放空間,實際在operator delete[ ]中調用operator delete,而operator delete函數實際通過free來釋放空間。
如果new和delete沒有匹配使用,產生後果?
void Test4()
{
Test* p1 = new Test;
free(p1);//不報錯
Test* p2 = new Test[10];
delete p2;//報錯
Test* p3 = new Test;
delete[] p3;//報錯 崩潰
}
結論:不管是new還是delete,只要涉及到[ ],必然會崩潰。
三、定位new表達式(placement-new)
含義:定位new表達式是在已分配的原始內存空間中調用構造函數初始化一個對象。
使用格式:new (place_address) type或者new (place_address) type(initializer-list)
place_address必須是一個指針,initializer-list是類型的初始化列表。
使用場景:定位new表達式在實際中一般是配合內存池使用。因爲內存池分配出的內存沒有初始化,所以如果是自定義類型的對象,需要使用new的定義表達式進行顯示調構造函數進行初始化。
void Test6()
{
// p1現在指向的只不過是與Test對象相同大小的一段空間,還不能算是一個對象,因爲構造函數沒有執行
Test* p1 = (Test*)malloc(sizeof(Test));
new(p1) Test; //如果Test類的構造函數有參數時,此處需要傳參
}
總結
malloc/free和new/delete的相同和不同
malloc/free和new/delete的共同點是:都是從堆上申請空間,並且需要用戶手動釋放。
不同的地方是:
- malloc和free是函數,new和delete是操作符。
- malloc申請的空間不會初始化,new可以初始化。
- malloc申請空間時,需要手動計算空間大小並傳遞,new只需在其後跟上空間的類型即可。
- malloc的返回值爲void*, 在使用時必須強轉,new不需要,因爲new後跟的是空間的類型。
- malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲異常。
- 申請自定義類型對象時,malloc/free只會開闢空間,不會調用構造函數與析構函數,而new在申請空間後會調用構造函數完成對象的初始化,delete在釋放空間前會調用析構函數完成空間中資源的清理。
- malloc申請的空間一定在堆上,但new不一定。