C/C++記憶體管理
- 前言
- 一、內置型別
- 二、自定義型別
- 三、operator new/operator delete
- 四、new和delete的總結
- 小測驗,畫出下面的st與pst所在堆疊幀位置
- 五、定位new運算式
- 六、malloc/free和new/delete的區別
- 總結
前言
本篇主要比較從malloc,free的組合到new,delete的變化,本章會簡述其程序為什么是這樣的!!
一、內置型別
#include<iostream>
using namespace std;
int main()
{
//C當中開辟十個整形空間
int* pi = (int*)malloc(sizeof(int) * 10);
free(pi);
pi = NULL;
//C++當中開辟十個整形空間
int* pi2 = new int[10];
delete pi2;
pi2 = nullptr;
return 0;
}

可以發現對于內置型別來說,我們現在能觀察到的就是new相對于malloc可以少寫一個回傳值,并且new是一個運算子,但是大體上我們觀察malloc和new是差不多的,看完后面再總結這點,
二、自定義型別
- 以堆疊為例,展開new對于自定義型別不同于malloc的不同之處,
class Stack
{
public:
Stack(int x = 10)
:a(new int[x])
,_capacity(x)
,_size(0)
{
cout << "Stack(int x = 10)" << endl;
}
private:
int _size;
int _capacity;
int* a;
};
int main()
{
//C
Stack* pst = (Stack*)malloc(sizeof(Stack));
free(pst);
pst = NULL;
//C++
Stack* pst2 = new Stack;
delete(pst2);
pst2 = nullptr;
return 0;
}

- 結論1:
可以從除錯當中看出,我們的new對于內置型別會呼叫物件的建構式進行初始化,并且還要說一點,他比起malloc還會對例外進行處理,當我們嘗試在32位的情況開辟2g的記憶體的時候,我們看看編譯器會報什么錯–拋例外

- 拋例外后的選擇
1.拋例外了,所以一般我們在new完之后要記得捕捉例外,因為有的時候有些地方申請記憶體失敗,但是他罪不至死,通過捕捉例外我們可以進行再處理!
2.不做任何處理

- 例外捕捉列印結果
bad allocation,就是我們捕捉例外列印的結果,從下面的代碼也可以看見我們的“haha’”并沒有列印,說明一出現例外的時候catch就把我們的例外捕獲了,
int main()
{
size_t i = 2;
try
{
char* pi = new char[i * 1024 * 1024 * 1024];
cout << "haha" << endl;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
}
三、operator new/operator delete
這個函式不是多載函式,不是多載函式,不是多載函式!!!
new和delete是用戶在動態申請記憶體和釋放所用到的運算子,operator new和operator delete是系統提供的全域函式,全域函式,全域函式!!!
廢話不多說,我們直接先來看看底層代碼實作
/*
operator new:該函式實際通過malloc來申請空間,當malloc申請空間成功時直接回傳;申請空間失敗,
嘗試執行空 間不足應對措施,如果改應對措施用戶設定了,則繼續申請,否則拋例外,
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) {
// try to allocate size bytes
void *p;
// ==0即申請失敗
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申請記憶體失敗了,這里會拋出bad_alloc 型別例外
static const std::bad_alloc nomem;//定義一個例外物件
_RAISE(nomem);//對例外做處理
}
return (p);
}


我們可以看出,operator new對比起malloc只是多了一個例外的處理,內部動態申請記憶體空間也是由malloc代勞,它的回傳值和malloc甚至都是一模一樣,所以呼叫operator new和malloc并太大區別,只是在拋例外這點做了處理,并且可以看出他沒有呼叫建構式初始化!
operator delete底層涉及到阻塞佇列,執行緒等概念,在這里我們初步理解為operator delete只是對free進行了一個封裝,用起來和free差不多即可,當中釋放空間的函式是用free的,
總結:假設對A類來說,new作用符就會干兩件事情,呼叫operator new,和呼叫A的建構式進行初始化,

四、new和delete的總結
1.對于內置型別來說,new和malloc,free和delete基本類似,不同的地方在于,new/delete是申請/釋放單個元素的空間,new[]和delete[]申請的是連續空間,而且new而在申請空間失敗時會拋例外,malloc會回傳NULL,
2.對于自定義型別來說,new的原理: 即呼叫operator new開空間并且在申請的空間上執行建構式,完成物件的構造,
delete的原理: 在空間上呼叫解構式,完成物件中資源的清理作業,再呼叫operatir delete函式釋放物件的空間,
其中new T[n]的原理: 呼叫operator new[]函式,operator new[]當中又是通過呼叫operator
new函式完成N個物件空間的申請,然后對這些空間執行N次建構式,delete[]的原理:釋放的物件空間上執行了N次解構式,完成N個物件中資源的清理,呼叫operator delete[]釋放空間,實際在operator delete[]中呼叫operator delete來釋放空間,
小測驗,畫出下面的st與pst所在堆疊幀位置
class Stack
{
public:
Stack(int cap)
:_a(new int[cap])
,_top(0)
,_cap(cap)
{}
private:
int* _a;
int _top;
int _cap;
};
int main()
{
Stack st(5);
Stack* pst = new Stack(5);
//....
delete pst;
pst = nullptr;
return 0;
}
需要注意這兩個實體化出來的物件一個是在堆疊,一個在堆,他們的宣告周期是不一樣的,但是他們的_a都是在堆上面,也證實了呼叫delete堆上的物件時,會先把它的資源(這里的_a)給清理了,這樣才不會造成記憶體泄漏,

五、定位new運算式
定位new的作用是在已分配的原始記憶體空間中呼叫建構式初始化一個物件
- 使用方式:兩種方式差異即type有無默認建構式!
new (place_address) type或者new (place_address) type(initializer-list)
place_address必須是一個指標,initializer-list是型別的初始化串列
定位new一般和記憶體池使用,正因為記憶體池分配出的記憶體沒有初始化,所以如果是自定義型別的物件,需要使用new的定義運算式進行顯示建構式進行初始化,
但我們這里沒涉及記憶體池,所以我們換一個測驗,列印一句話證明呼叫成功即可,
class Stack
{
public:
Stack(int cap)
:_a(new int[cap])
,_top(0)
,_cap(cap)
{
cout << "完成一次呼叫定位new\n";
}
~Stack()
{
free(_a);
_a = nullptr;
cout << "完成一次釋放空間\n";
}
private:
int* _a;
int _top;
int _cap;
};
int main()
{
Stack* pst = (Stack*)malloc(sizeof(Stack));
new(pst) Stack(5);//這里呼叫Stack的構造需要傳參
delete(pst);
pst = nullptr;
return 0;
}

六、malloc/free和new/delete的區別
我們從三點出發:特點和用法,底層原理區別,處理錯誤的方式,
- malloc/free是函式,new/delete是運算子(特點)
- malloc申請空間需要手動計算空間,new的話只需要空間的型別就可以(用法)
- malloc的回傳值是void,new的回傳值就是型別的指標(用法),
- malloc失敗回傳NULL,delete失敗拋例外(處理錯誤的方式)
- 申請自定義型別物件時,malloc/free只會開辟空間,new會開辟好空間呼叫建構式,delete會先呼叫解構式再釋放空間,new在申請空間用到了malloc,delete用到了free,(底層原理區別)
呼叫new的時候我們都做了什么?
我們呼叫new相當于呼叫operator new先把空間開出來(拋例外),new當中實作了定位new顯示呼叫建構式,然后還利用了模板把回傳值處理了,
編輯器的話指令少了定位new直接呼叫,但我們呼叫的時候就要顯式呼叫,
總結
C++記憶體管理結束啦!!下一章節將會進行模板的講述,講述完之后就是STL,相當于C++終于可以入門啦!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/337752.html
標籤:其他
上一篇:【LeetCode系列】楊輝三角
下一篇:C語言基礎之分支陳述句
