C++中動(dòng)態(tài)內(nèi)存管理的實(shí)現(xiàn)
一. C&C++內(nèi)存分布
- 棧:非靜態(tài)局部變量/函數(shù)參數(shù)/返回值等,棧是向下增長的。
- 內(nèi)存映射段是高效的I/O映射方式,用于裝載一個(gè)共享的動(dòng)態(tài)內(nèi)存庫。用戶可使用系統(tǒng)接口創(chuàng)建共享共享內(nèi)存,做進(jìn)程間通信。(Linux詳細(xì)講解)
- 堆:程序運(yùn)行時(shí)動(dòng)態(tài)內(nèi)存分配,堆是可以上增長的。
- 數(shù)據(jù)段、靜態(tài)區(qū):存儲(chǔ)全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)。
- 代碼段、常量區(qū):可執(zhí)行的代碼/只讀常量。
int globalVar = 1; static int staticGlobalVar = 1; void Test() { static int staticVar = 1; int localVar = 1; int num1[10] = { 1,2,3,4 }; }
globalVar、staticGlobalVar、staticVar在靜態(tài)區(qū)
localVar、num1在棧
void Test { char char2[] = "abcd"; const char* pChar3 = "abcd"; // 常量字符串,如果不加const則權(quán)限放大,報(bào)錯(cuò) int* ptr1 = (int*)malloc(sizeof(int) * 4); int* ptr2 = (int*)calloc(4, sizeof(int)); int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); free(ptr1); free(ptr3); }
char2、*char2、pChar3、ptr1在棧
*pChar3在常量區(qū)
*ptr1在堆
二. C++內(nèi)存管理方式
C主要靠malloc/calloc/realloc/free等函數(shù)手段完成
C++通過 new 和 delete 操作符進(jìn)行動(dòng)態(tài)內(nèi)存管理
1. 內(nèi)置類型
內(nèi)置類型C&C++沒太大區(qū)別。
C++的 new delete 不用算多少字節(jié)、不用強(qiáng)轉(zhuǎn)
new、delete 申請(qǐng)和釋放的是單個(gè)元素的空間
new[ ]、delete[ ] 申請(qǐng)的是連續(xù)空間
int main() { // C int* p1 = (int*)malloc(sizeof(int)); // 隨機(jī)值 free(p1); // C++ 動(dòng)態(tài)申請(qǐng)一個(gè)int類型的空間 int* p2 = new int; // 隨機(jī)值 delete p2; // C int* p3 = (int*)malloc(sizeof(int)*10); // 全是隨機(jī)值 free(p3); // C++ 動(dòng)態(tài)申請(qǐng)10個(gè)int類型的空間 int* p4 = new int[10]; // 全是隨機(jī)值 delete[] p4; // C++ 動(dòng)態(tài)申請(qǐng)一個(gè)int類型的空間并初始化為10 int* p5 = new int(10); delete p5; int* p6 = new int[10]{1,2,3}; // 其余是0 delete[] p6; return 0; }
2. 自定義類型
C:malloc 只是單純開空間,不初始化;free 不清理資源
C++:new 出來就調(diào)用構(gòu)造函數(shù)初始化;delete 調(diào)用析構(gòu)函數(shù)
struct ListNode { int _val; struct ListNode* _next; ListNode(int x) // 構(gòu)造函數(shù) :_val(x) , _next(NULL) {} }; struct ListNode* BuyListNode(int x) { // 單純開空間 struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode)); // 檢查 newnode->_next = NULL; newnode->_val = x; return newnode; } int main() { struct ListNode* n1 = BuyListNode(1); struct ListNode* n2 = BuyListNode(2); struct ListNode* n3 = BuyListNode(3); // 開空間+調(diào)用構(gòu)造函數(shù)初始化 ListNode* nn1 = new ListNode(1); ListNode* nn2 = new ListNode(2); ListNode* nn3 = new ListNode(3); return 0; }
class A { public: A(int a = 0) : _a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; int main() { A* p1 = (A*)malloc(sizeof(A)); A* p2 = new A(1); free(p1); delete p2; A* p5 = (A*)malloc(sizeof(A) * 10); A* p6 = new A[4]; free(p5); delete[] p6; return 0; }
p6 全初始化成 0,因?yàn)闃?gòu)造函數(shù)給了缺省參數(shù)
如果沒有默認(rèn)構(gòu)造函數(shù)
class A { public: A(int a) : _a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; int main() { // A* p6 = new A[4]; // 報(bào)錯(cuò):“A”: 沒有合適的默認(rèn)構(gòu)造函數(shù)可用 A* p6 = new A[4]{ 1,2,3,4 }; A* p6 = new A[4]{ A(1),A(2),A(3),A(4) }; // 給4個(gè)匿名對(duì)象 delete[] p6; return 0; }
如果多個(gè)參數(shù)
class A { public: A(int a, int b) : _a(a) { cout << "A():" << this << endl; } A(const A& aa) : _a(aa._a) { cout << "A(const A& aa):" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; int main() { A* p2 = new A(1, 1); delete p2; A* p6 = new A[4]{ A(1,1),A(2,2),A(3,3),A(4,4) }; delete[] p6; return 0; }
構(gòu)造+拷貝構(gòu)造-->優(yōu)化為直接構(gòu)造
此時(shí),沒有默認(rèn)構(gòu)造,必須傳4個(gè)匿名對(duì)象。如果給了缺省參數(shù),有了默認(rèn)構(gòu)造,可以少傳
三. new delete 底層
operator new、operator delete 函數(shù):
他們不是運(yùn)算符重載,是庫里的全局函數(shù)。他們不是給程序員用的,是給 new、delete用的
C語言處理失敗,返回錯(cuò)誤碼。C語言喜歡用 malloc,申請(qǐng)失敗返回 0(NULL)
C++處理失敗,拋異常
C++源代碼中:
operator new 實(shí)際也是通過malloc來申請(qǐng)空間。若malloc申請(qǐng)空間成功就直接返回;否則執(zhí)行用戶提供的空間不足應(yīng)對(duì)措施,如果用戶提供該措施就繼續(xù)申請(qǐng),否則就拋異常
operator delete 最終是通過free來釋放空間的(malloc、free只是隱藏起來了,不是沒用了)
new[ ]:
1. 調(diào)用operator new[]函數(shù),在operator new[]中實(shí)際調(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í)際在operator delete[]中調(diào)用operator delete來釋放空間
用 Stack 理解
int main() { // 申請(qǐng)一個(gè)堆上的棧對(duì)象 Stack* p1 = new Stack; delete p1; return 0; }
用 new 就不用加 if (NULL==_array) 這種檢查了。new 失敗拋異常,我們?cè)谒?new 的地方 try catch捕獲就行
int main() { try { // 申請(qǐng)一個(gè)堆上的棧對(duì)象 Stack* p1 = new Stack; delete p1; } catch (const exception& e) { cout << e.what() << endl; } return 0; }
這里的 try catch 不僅能捕獲 main 函數(shù)里的;還能捕獲構(gòu)造函數(shù)里面的異常
只要是在范圍內(nèi),直接、間接調(diào)用的都可以捕獲
四. 定位 new 表達(dá)式(placement-new)
new 的其他操作
定位new表達(dá)式是對(duì)已有空間調(diào)用構(gòu)造函數(shù) 初始化一個(gè)對(duì)象
格式:new (指針) 類型 或 new (指針) 類型(類型的初始化列表)
int main() { // p1現(xiàn)在指向的只不過是與A對(duì)象相同大小的一段空間,還不能算是一個(gè)對(duì)象,因?yàn)闃?gòu)造函數(shù)沒有執(zhí)行 A* p1 = (A*)malloc(sizeof(A)); // 顯示調(diào)用構(gòu)造函數(shù) new(p1)A; // 注意:如果A類的構(gòu)造函數(shù)有參數(shù)時(shí),此處需要傳參 new(p1)A(1); // 顯示調(diào)用析構(gòu)函數(shù) p1->~A(); free(p1); // 自定義類型的對(duì)象才能自動(dòng)調(diào)用構(gòu)造、析構(gòu)函數(shù)。p1是內(nèi)置類型,必須顯示調(diào)用析構(gòu) return 0; }
應(yīng)用場(chǎng)景:池化技術(shù):內(nèi)存池、線程池、連接池
定位new表達(dá)式 在實(shí)際中一般是配合內(nèi)存池使用。因?yàn)?span>內(nèi)存池分配出的內(nèi)存沒有初始化,所以如果是自定義類型的對(duì)象,需要使用new的定義表達(dá)式進(jìn)行顯示調(diào)構(gòu)造函數(shù)進(jìn)行初始化
需要頻繁申請(qǐng)和釋放內(nèi)存。new 是直接找堆,路徑長、麻煩
建池子,提前申請(qǐng)一大塊內(nèi)存放到池子里。要的時(shí)候直接找池子要,池子沒了再去找堆。效率高
五. 內(nèi)存泄露
http://www.dbjr.com.cn/program/334321hr3.htm
到此這篇關(guān)于C++中動(dòng)態(tài)內(nèi)存管理的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 動(dòng)態(tài)內(nèi)存管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C/C++ 傳遞動(dòng)態(tài)內(nèi)存的深入理解
- C++動(dòng)態(tài)內(nèi)存分配超詳細(xì)講解
- 詳解C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配
- C++ 中繼承與動(dòng)態(tài)內(nèi)存分配的詳解
- 詳解C++動(dòng)態(tài)內(nèi)存管理
- c++ 動(dòng)態(tài)內(nèi)存分配相關(guān)總結(jié)
- c++動(dòng)態(tài)內(nèi)存管理與智能指針的相關(guān)知識(shí)點(diǎn)
- 一文搞懂C++ 動(dòng)態(tài)內(nèi)存
- C++使用動(dòng)態(tài)內(nèi)存分配的原因解說
- C++中為什么要使用動(dòng)態(tài)內(nèi)存
- c++動(dòng)態(tài)內(nèi)存管理詳解(new/delete)
相關(guān)文章
解析如何用指針實(shí)現(xiàn)整型數(shù)據(jù)的加法
本篇文章是對(duì)用指針實(shí)現(xiàn)整型數(shù)據(jù)加法的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05實(shí)現(xiàn)Dijkstra算法最短路徑問題詳解
這篇文章主要介紹了實(shí)現(xiàn)Dijkstra算法最短路徑問題詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08C語言枚舉(enum)和聯(lián)合(union)實(shí)例分享
在本篇文章里小編給大家整理了關(guān)于C語言枚舉(enum)和聯(lián)合(union)實(shí)例內(nèi)容,需要的朋友們可以學(xué)習(xí)下。2020-03-03