在C語言當中, 動態內存的管理方式分爲 malloc / calloc / realloc 和 free
-
malloc函數可以在堆上申請指定字節的內存空間, 申請成功返回申請到的空間的首地址, 如果申請失敗會返回NULL.
void* malloc(size_t size);
注意malloc申請到的空間是沒有經過初始化的 -
calloc函數與malloc的功能相似, 但是calloc申請到的空間是經過初始化的, 初始化內容爲0.
void* malloc(size_t, num, size_t size);//申請num個字節大小爲size的空間
-
realloc函數
void* realloc (void* ptr, size_t size);
realloc函數會改變ptr所指向空間的內存大小, 其中ptr必須是指向堆內存空間的指針, 也就是由malloc/calloc/realloc函數所分配空間的指針. 如果size小於或等於之前ptr所指向空間的大小, 就會保持原來狀態不變, 如果size大於之前ptr所指向空間的大小, 會重新開闢一塊大小爲size的內存空間, 並將原來所指向空間中的內容複製到新的空間上. realloc函數所申請的空間也是沒有經過初始化的.
C語言內存管理方式在C++當中仍然可以繼續使用, 但有些地方用起來並不方便, 比如開闢自定義類型的空間, 因此在C++當中又提出了自己的誒存管理方式, 通過new和delete操作符進行動態內存管理.
new / delete對於內置類型的操作
內置類型就是 int, char, float, double等
void test()
{
//動態申請一個int類型的空間
int* pi = new int;//申請一個4字節整型空間
//動態申請一個int類型的空間並初始化
int* pi2 = new int(2);//申請一個4字節整型空間並賦值爲2
//動態申請int類型的數組
int* pi3 = new int[5];//申請了一個長度爲5的整型數組
delete pi;
delete pi2;
delete[] pi3;
}
注意:
-
注意申請空間時 () 和 [] 的區別, ()是將申請到的空間初始化, 而 [] 是申請數組時用的;
-
對應的釋放空間時, 釋放連續空間(即數組)時記得要用到 delete[].
-
int* pi4 = new int[5](1);//這樣寫是不可以的
其實看到這裏, 我們就會想, 在C語言當中已經有了 malloc / calloc / realloc / free這些函數, 那爲什麼在C++當中又要出現 new / delete, new [] / delete[]?
首先, 針對內置類型來說, 無論使用 new 還是 malloc 等其實都是一樣的.
對於自定義類型來說, 就會不一樣了. 比如下面的例子:
自定義類型 A
class A
{
public:
A(int x = 10) :
_val(x)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _val;
};
用malloc申請空間
void test2()
{
A* pA_C = (A*)malloc(sizeof(A));
cout << pA_C << endl;
cout << pA_C->getVal() << endl;
free(pA_C);
}
輸出結果:
空間申請成功, 類成員無法初始化打印隨機值.
接下來使用 new / delete
void test1()
{
A* pA_CPP = new A;//申請單個A類型的對象
cout << pA_CPP << endl;
cout << pA_CPP->getVal() << endl;
delete pA_CPP;//釋放掉這片空間
}
輸出結果:
可以很清楚的看到, new在針對自定義類型申請空間的時候, 會自動調用構造函數, 構造函數又幫我們完成了對類成員_val的初始化, 而delete會自動調用析構函數完成資源的清理工作.
我們可以進一步驗證一下, new / delete 針對自定義類型的操作, 接下來我們申請一個A類型的對象數組
// new / delete
A* pA_CPP_ARR = new A[10];
delete[] pA_CPP_ARR;
調用10次構造函數, 10次析構函數
// malloc
A* pA_C_ARR = (A*)malloc(sizeof(A)* 10);
free(pA_C_ARR);
malloc只開闢空間.
結論: 建議在C++當中使用 new / delete
operator new和operator delete函數
看到operator我們一定會想到運算符重載, 某種角度來說這樣理解也可以, 但是這裏我們要儘量將其看做一個整體, 看做函數來理解它.
同樣, 先給一個自定義類型
class A
{
public:
A(int x = 10) :
_val(x)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int getVal()
{
return _val;
}
private:
int _val;
};
接下來申請空間
void test1()
{
A* ptr1 = (A*)malloc(sizeof(A));
A* ptr2 = new A(1);
A* ptr3 = (A*)operator new(sizeof(A));
free(ptr1);
delete ptr2;
operator delete(ptr3);
}
執行結果
均申請空間成功, 通過上面的講解我們知道, new / delete 會調用構造和析構, 那麼operator new / operator delete 又是幹什麼用的呢?它與malloc / free的區別在哪裏呢?
-
首先operator new / operator delete的使用與 malloc / free相同
void* operator new(size_t size); operator delete(void* ptr);
-
舉個例子來看operator new與malloc的區別
//用malloc去申請空間 size_t size = 2; void* ptr1 = malloc(size * 1024 * 1024 * 1024); cout << ptr1 << endl;//申請失敗, 返回NULL(0)
//用operator new去申請空間
void* ptr2 = operator new(size * 1024 * 1024 * 1024);
cout << ptr2 << endl;
程序會崩掉. 因爲operator new對比malloc, operator new申請空間失敗會拋異常(面向對象的錯誤處理方式), 必須跳到跳到catch的地方, 所以代碼要修改爲下面這樣
try
{
void* ptr2 = operator new(size * 1024 * 1024 * 1024);
cout << ptr2 << endl;
}
catch (exception& e)
{
cout << e.what() << endl;
}
運行結果如圖:
總結: operator new 對比 malloc 使用方式相同, 處理錯誤的方式不同
new本身就是爲了C++面向對象編程而產生的, 因此從malloc->operator new->new是層層遞進的
malloc
operator new ==> malloc + 申請空間失敗拋異常的實現
new ==> operator new + 自動調用構造函數
對應的擇優free, operator delete, delete
而delete 比起 free 不一樣的地方就是 delete 自調用析構函數清理資源.
operator delete 與 free沒有區別.因爲釋放空間失敗會自動終止程序.
new / delete原理
new原理
- 調用 operator new函數申請空間
- 在申請到的空間上執行自定義類型的構造函數, 完成對象的構造
delete原理
- 在申請到的空間上執行自定義類型的析構函數, 完成資源的清理工作
- 調用operator delete函數釋放對象的空間
new T[N]原理
- 實際調用operator new函數申請N個對象的空間
- 在申請到的空間執行N次自定義類型的構造函數
delete[N]原理
- 在申請到的空間上執行N次自定義類型的析構函數, 完成N個對象的資源清理
- 實際調用 operator delete函數釋放空間
定位new表達式
定位new表達式就是在已分配的內存空間上調用構造函數初始化一個對象
比如:
class Test
{
public:
Test(int x = 10) :
_a(x)
{
cout << "Test" << endl;
}
int GetVal()
{
return _a;
}
~Test()
{
cout << "~Test()" << endl;
}
private:
int _a;
};
void test3()
{
Test* pT = (Test*)malloc(sizeof(Test));//申請好的內存
new(pT) Test(10);//定位new表達式
cout << pT->GetVal() << endl;
}
總結
malloc / free和new / delete的區別
- malloc / free是函數, new / delete是操作符
- malloc申請空間不會初始化, new可以(自動調用構造函數)
- malloc函數使用時需要手動計算類型大小(sizeof(T)), new不需要, 直接在new後面跟所需空間類型即可
- malloc函數申請空間返回值爲void*, 因此使用時要做類型強轉, new不需要
- malloc申請失敗返回NULL(0), new申請失敗拋異常
- 最重要的針對自定義類型, malloc / free只申請和釋放空間, new會自動調用構造函數完成對象的初始化, delete會調用自定義類型的析構函數完成資源清理, 再釋放空間.