C++中new和delete匹配使用過程詳解
C語(yǔ)言的動(dòng)態(tài)內(nèi)存管理函數(shù)(malloc、calloc、realloc、free) 雖然可以繼續(xù)在 C++ 使用,但是對(duì)于自定義類型成員而言,這些函數(shù)不會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù),于是 C++ 增加了 new 和 delete 關(guān)鍵字
一、new和delete的使用
new 和 delete 用于在堆上申請(qǐng)或釋放一個(gè)元素的空間,new[] 和 delete[] 用于在堆上申請(qǐng)或釋放一塊連續(xù)的空間,對(duì)于自定義類型空間的開辟,new 和 delete 還會(huì)調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)
#include <iostream> using namespace std; class Demo { public: Demo(int a1 = 10, int a2 = 20) : _a1(a1) , _a2(a2) { cout << "Demo()" << endl; } void print() { cout << _a1 << " " << _a2 << endl; } ~Demo() { cout << "~Demo()" << endl; } private: int _a1; int _a2; }; void printIntArr(int* arr, int len) { for (int i = 0; i < len; ++i) { cout << arr[i] << " "; } cout << endl; } void printDemoArr(Demo* arr, int len) { for (int i = 0; i < len; ++i) { arr[i].print(); } cout << endl; } int main() { //用 new 申請(qǐng)一個(gè)內(nèi)置類型變量的空間 int* pint1 = new int; cout << *pint1 << endl; //輸出 -842150451 //使用括號(hào)中的值初始化變量 int* pint2 = new int(5); cout << *pint2 << endl; //輸出 5 //用 delete 釋放一個(gè)變量的空間 delete pint1; delete pint2; //用 new 申請(qǐng)一個(gè)自定義類型對(duì)象的空間,申請(qǐng)后會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù) Demo* pd1 = new Demo; //輸出 Demo() pd1->print(); //輸出 10 20 //自定義類型會(huì)根據(jù)括號(hào)中的參數(shù)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù) Demo* pd2 = new Demo(5, 5); //輸出 Demo() pd2->print(); //輸出 5 5 //用 delete 釋放一個(gè)變量的空間,釋放前會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù) delete pd1; //輸出 ~Demo() delete pd2; //輸出 ~Demo() //對(duì)內(nèi)置類型用 new[] 開辟一塊連續(xù)的空間 int* pint3 = new int[5]; //[]中表示開辟整形的個(gè)數(shù) printIntArr(pint3, 5); //輸出 -842150451 -842150451 -842150451 -842150451 -842150451 //用花括號(hào)中的值初始化開辟的連續(xù)空間,未給值的為 0 int* pint4 = new int[5]{ 1, 2, 3, 4 }; printIntArr(pint4, 5); //輸出 1 2 3 4 0 //對(duì)內(nèi)置類型用 delete[] 釋放一塊連續(xù)的空間 delete[] pint3; delete[] pint4; //對(duì)自定義類型用 new[] 開辟一塊連續(xù)的空間 //申請(qǐng)后會(huì)對(duì)空間自動(dòng)調(diào)用構(gòu)造函數(shù) 5 次 Demo* pd3 = new Demo[5]; //輸出 5 行 Demo() printDemoArr(pd3, 5); //輸出 5 行 10 20 //用花括號(hào)中的值初始化開辟的連續(xù)空間 //花括號(hào)中如果用小括號(hào)會(huì)被認(rèn)為是逗號(hào)表達(dá)式,會(huì)去調(diào)用單參的構(gòu)造函數(shù) //調(diào)用多參構(gòu)造函數(shù)應(yīng)在花括號(hào)中使用花括號(hào),未給的值根據(jù)構(gòu)造函數(shù)決定 Demo* pd4 = new Demo[5]{ (1, 2), {5}, {5, 10}}; //輸出 5 行 Demo() printDemoArr(pd4, 5); //輸出 第一行 2 20,第二行 5 10 第三行 5 10,兩行 10 20 //對(duì)自定義類型用 delete[] 釋放一塊連續(xù)的空間 //釋放之前會(huì)對(duì)空間自動(dòng)調(diào)用析構(gòu)函數(shù) 5 次 delete[] pd3; //輸出 5 行 ~Demo delete[] pd4; //輸出 5 行 ~Demo return 0; }
二、operator new和operator delete函數(shù)
operator new 和 operator delete 是系統(tǒng)提供的全局函數(shù),不是 new 和 delete 的運(yùn)算符重載函數(shù)
- operator new 底層是通過 malloc 函數(shù)來申請(qǐng)空間,當(dāng)空間申請(qǐng)成功時(shí)直接返回,失敗時(shí)拋出異常(不會(huì)返回 nullptr),operator new 函數(shù)可以像 malloc 一樣使用,只是失敗時(shí)的處理不同
- operator delete 和 free 底層都是是通過 _free_dbg 函數(shù)釋放空間,只不過 operator delete 會(huì)對(duì)釋放前后進(jìn)行一些檢查
#include <iostream> using namespace std; int main() { //operator new 和 malloc 使用方法一樣 //operator new 申請(qǐng)空間失敗時(shí)拋異常 int* pi = (int*)operator new(sizeof(int) * 4); //operator delete 和 free 使用方法一樣,都會(huì)調(diào)用 _free_dbg //operator delete 在釋放空間時(shí)會(huì)做一些檢查 operator delete(pi); return 0; }
operator new[] 和 operator delete[] 也是系統(tǒng)提供的全局函數(shù),內(nèi)部是通過調(diào)用 operator new 和 operator delete 函數(shù)
三、new和delete的實(shí)現(xiàn)原理
如果是內(nèi)置類型,new 和 delete 調(diào)用 operator new 和 operator delete,new[] 和 delete[] 調(diào)用 operator new[] 和 operator delete[]
如果是自定義類型:
#include <iostream> using namespace std; class Demo { public: Demo(int a1 = 10, int a2 = 20); ~Demo(); private: int _a1; int _a2; }; Demo::Demo(int a1, int a2) : _a1(a1) , _a2(a2) { cout << "Demo()" << endl; } Demo::~Demo() { cout << "~Demo()" << endl; } int main() { Demo* pd1 = new Demo(5, 5); delete pd1; Demo* pd2 = new Demo[2]{ {1, 2}, {2, 3} }; delete[] pd2; return 0; }
new:
1. 調(diào)用 operator new 函數(shù)申請(qǐng)空間
2. 在申請(qǐng)的空間上執(zhí)行構(gòu)造函數(shù),完成對(duì)象的構(gòu)造
delete:
1. 在空間上執(zhí)行析構(gòu)函數(shù),完成對(duì)象中資源的清理工作
2. 調(diào)用operator delete函數(shù)釋放對(duì)象的空間
new 類型[N]:
1. 調(diào)用operator new[] 函數(shù),實(shí)際上是在 operator new[] 中調(diào)用 operator new 函數(shù)完成 N 個(gè)對(duì)象空間的申請(qǐng)
2. 在申請(qǐng)的空間上執(zhí)行 N 次構(gòu)造函數(shù)
delete[]:
1. 在釋放的對(duì)象空間上執(zhí)行 N 次析構(gòu)函數(shù),完成 N 個(gè)對(duì)象中資源的清理
2. 調(diào)用 operator delete[] 釋放空間,實(shí)際上時(shí)在 operator delete[] 中調(diào)用 operator delete 來釋放空間
四、申請(qǐng)空間和釋放空間應(yīng)配套使用
malloc/free、new/delete、new[]/delete[] 需要配套使用,否則總會(huì)有出問題的時(shí)候
下述代碼不會(huì)報(bào)錯(cuò),會(huì)產(chǎn)生內(nèi)存泄漏
#include <iostream> using namespace std; class Stack { public: Stack(int capacity = 4) : _a(new int[capacity]) , _top(0) , _capacity(capacity) { } ~Stack() { if (_a) { delete[] _a; _top = _capacity = 0; } } private: int* _a; int _top; int _capacity; }; int main() { Stack* ps = new Stack; //free(ps); //內(nèi)存泄漏 //delete 釋放內(nèi)存之前會(huì)調(diào)用析構(gòu)函數(shù) delete ps; //正確寫法 return 0; }
下述代碼在 vs2022 下會(huì)崩潰
#include <iostream> using namespace std; class A { public: A(int a = 0) : _a(a) { cout << "A():" << endl; } ~A() { cout << "~A():" << endl; } private: int _a; }; int main() { A* p1 = new A[10]; //free(p1); //崩潰 delete[] p1; //正確寫法 A* p2 = new A[10]; //delete p2; //崩潰 delete[] p2; //正確寫法 return 0; }
注意:不同的編譯器處理可能不同,這里只代表在 vs2022 編譯器中
五、定位new表達(dá)式
定位 new 表達(dá)式是在已開辟好的原始內(nèi)存空間上調(diào)用構(gòu)造函數(shù)初始化一個(gè)對(duì)象,使用格式:
new(place_address)type 或者 new(place_address)type(initializer-list)
place_address 必須是一個(gè)指針,initializer-list 是類型的初始化列表
定位 new 表達(dá)式在實(shí)際中一般是配合 內(nèi)存池 使用,因?yàn)閮?nèi)存池分配出的內(nèi)存沒有初始化,并且構(gòu)造函數(shù)不可以顯示調(diào)用,所以如果是自定義類型的對(duì)象,需要使用定位 new 以進(jìn)行顯示調(diào)用構(gòu)造函數(shù)進(jìn)行初始化
#include <iostream> using namespace std; class A { public: A(int a = 0) : _a(a) { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } private: int _a; }; //定位 new 又叫 replacement new int main() { //p1 現(xiàn)在指向的只是與 A 對(duì)象相同大小的一段空間,并不是一個(gè)對(duì)象,因?yàn)闆]有調(diào)用構(gòu)造函數(shù) A* p1 = (A*)malloc(sizeof(A)); new(p1)A; //調(diào)用無(wú)參的構(gòu)造函數(shù) 輸出 A() //可以手動(dòng)調(diào)用析構(gòu)函數(shù),然后釋放空間 p1->~A(); //輸出 ~A() free(p1); //p2 現(xiàn)在指向的只是與 A 對(duì)象相同大小的一段空間,并不是一個(gè)對(duì)象,因?yàn)闆]有調(diào)用構(gòu)造函數(shù) A* p2 = (A*)operator new(sizeof(A)); new(p2)A(10); //10 是參數(shù),可以根據(jù)參數(shù)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù) 輸出 A() p2->~A(); //輸出 ~A() operator delete(p2); return 0; }
六、malloc/free和new/delete的區(qū)別
malloc/free 和 new/delete 的共同點(diǎn)是:都是從堆上申請(qǐng)空間,并且需要用戶手動(dòng)釋放
不同的地方是:
- malloc 和 free 是函數(shù),new 和 delete 是運(yùn)算符
- malloc 申請(qǐng)的空間不會(huì)初始化,new 可以初始化
- malloc 申請(qǐng)空間時(shí),需要手動(dòng)計(jì)算空間大小并傳遞,new 只需在其后跟上空間的類型,如果是多個(gè)對(duì)象,[] 中指定對(duì)象個(gè)數(shù)即可
- malloc 的返回值為 void*,接收時(shí)必須強(qiáng)制類型轉(zhuǎn)換,new 不需要,因?yàn)?new 后跟的是空間的類型
- malloc 申請(qǐng)空間失敗時(shí),返回的是NULL,因此使用時(shí)必須判空,new 不需要,但是 new 需要捕獲異常
- 申請(qǐng)自定義類型對(duì)象時(shí),malloc/free 只會(huì)開辟空間,不會(huì)調(diào)用構(gòu)造函數(shù)與析構(gòu)函數(shù),而 new 在申請(qǐng)空間后會(huì)調(diào)用構(gòu)造函數(shù)完成對(duì)象的初始化,delete 在釋放空間前會(huì)調(diào)用析構(gòu)函數(shù)完成空間中資源的清理
到此這篇關(guān)于C++中new和delete匹配使用過程詳解的文章就介紹到這了,更多相關(guān)C++ new與delete內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VScode搭建OpenCV環(huán)境的詳細(xì)步驟
用vscode來寫opencv代碼需要自己編譯OpenCV,主要用到MinGW-w64和CMake工具。接下來通過本文給大家介紹VScode搭建OpenCV環(huán)境的相關(guān)知識(shí),需要的朋友可以參考下2021-11-11C指針原理教程之語(yǔ)法樹及其實(shí)現(xiàn)
本文給大家分享的是如何使用C語(yǔ)言的指針原來來實(shí)現(xiàn)語(yǔ)法樹,并給大家提供了詳細(xì)的實(shí)例代碼,希望大家能夠喜歡2019-02-02Visual C++中Tab View的多種實(shí)現(xiàn)方法
這篇文章主要介紹了Visual C++中Tab View的多種實(shí)現(xiàn)方法,包括了CTabCtrl控件、CSheetCtrl標(biāo)簽選擇窗口以及靜態(tài)分割窗口等實(shí)現(xiàn)Tab View的方法,需要的朋友可以參考下2014-10-10Qt事件過濾實(shí)現(xiàn)點(diǎn)擊圖片的放大和縮小
這篇文章主要為大家詳細(xì)介紹了Qt事件過濾實(shí)現(xiàn)點(diǎn)擊圖片的放大和縮小,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08C++設(shè)計(jì)模式之組合模式(Composite)
這篇文章主要為大家詳細(xì)介紹了C++設(shè)計(jì)模式之組合模式Composite,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04VS未找到框架“.NETFramework,Version=v4.6.1”引用程序集的解決辦法
本文主要介紹了VS未找到框架“.NETFramework,Version=v4.6.1”引用程序集的解決辦法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10C++控制臺(tái)實(shí)現(xiàn)密碼管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++控制臺(tái)實(shí)現(xiàn)密碼管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11