C++的內(nèi)存管理詳細解釋
一、C/C++內(nèi)存分布
- 棧又叫堆棧,非靜態(tài)局部變量/函數(shù)參數(shù)/返回值等等,棧是向下增長的。
- 內(nèi)存映射段是高效的I/O映射方式,用于裝載一個共享的動態(tài)內(nèi)存庫。用戶可使用系統(tǒng)接口創(chuàng)建共享共享內(nèi)存,做進程間通信。
- 堆用于程序運行時動態(tài)內(nèi)存分配,堆是可以向上增長的。
- 數(shù)據(jù)段–存儲全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)。
- 代碼段–可執(zhí)行的代碼/只讀常量。
二、C語言中動態(tài)內(nèi)存管理方式:
1、malloc/calloc/realloc區(qū)別:
共同點:
- 都是C語言中用來進行動態(tài)內(nèi)存申請的庫函數(shù),申請的空間都在堆上,用完之后必須使用free來進行釋放
- 返回值類型都是void*在接受返回的地址時必須要進行強轉(zhuǎn)
- 如果申請空間成功:返回的是空間的首地址,如果失敗返回的是NULL
不同點:
- malloc:返回值類型void*
在接受返回的空間地址時必須要進行強轉(zhuǎn)
成功:空間首地址 失敗:NULL
參數(shù):申請的空間所占總的字節(jié)數(shù)
申請的空間在堆上,使用完成后必須要使用free來進行釋放
- calloc:返回值一致
參數(shù)列表:參數(shù)一表示元素的個數(shù);參數(shù)二表示單個類型的字節(jié)數(shù)
功能:與malloc基本相同,但是calloc會對其申請的空間進行初始化
- realloc(void* p, size_t size):將p所指向空間的大小調(diào)整到size字節(jié)
p指向的是NULL:該函數(shù)的類似malloc
假設(shè):p所指向的空間總共占o(jì)ld個字節(jié)
size <= old:將p所指向的空間縮小到size個字節(jié)---->直接返回值p所指向空間的首地址
size > old:將p所指向的空間擴增到size個字節(jié)
大一點:返回原空間的首地址
大的多的多:申請新空間;將舊空間中元素拷貝到新空間;釋放舊空間;返回新空間的首地址
三、C++中動態(tài)內(nèi)存管理:new/delete
C++為什么要搞一套動態(tài)內(nèi)存管理?
首先:C語言中的動態(tài)內(nèi)存管理方式在C++中仍然可以使用
原因:
1、C語言中的方式比較麻煩—需要用戶手動算字節(jié)數(shù),需要對返回結(jié)果強轉(zhuǎn),需要判空,需要包含頭文件
2、malloc、free:不會調(diào)用構(gòu)造函數(shù)/析構(gòu)函數(shù);new、delete:在進行空間申請和釋放時是會調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)的
// C++中動態(tài)內(nèi)存管理方式:new/delete----申請單個類型的空間 // new[]/delete[]-----申請釋放一段連續(xù)的空間 //注意:1、new/delete不是函數(shù),是C++中的關(guān)鍵字||操作符 // 2、new的空間必須要有delete釋放 new[]必須使用delete[]釋放 class Test { public: Test() :_t(10) { cout << "Test():" << this << endl; } ~Test() { cout << "~Test():" << this << endl; } int _t; }; //new/delete和new[]/delete[]使用說明 void Test1() { int* p1 = new int; int* p2 = new int(100); int* p3 = new int[10]; int* p4 = new int[10]{ 1,2,3,4,5,6,7,8,9,0 }; delete p1; delete p2; delete[] p3; delete[] p4; } void Test2() { //malloc并不是創(chuàng)建了一個Test類型的對象,只是在堆上申請了一塊與Test類型大小相同的一塊空間 //因為:malloc不調(diào)用構(gòu)造函數(shù) Test* p1 = (Test*)malloc(sizeof(Test));//malloc不會調(diào)用構(gòu)造函數(shù) if (nullptr == p1) return; //真正創(chuàng)建了一個對象,該對象的空間在堆上 Test* p2 = new Test;//new在申請空間期間會調(diào)用構(gòu)造函數(shù) free(p1);//:在釋放空間期間,不會調(diào)用析構(gòu)函數(shù) delete p2;//:在釋放空間期間,會調(diào)用對象的析構(gòu)函數(shù) } /* 在C++中,如果想要在堆上申請空間: 1、采用C語言中的malloc、calloc、realloc,但是并不能申請對象的空間 2、采用new/new[]---可以調(diào)用構(gòu)造函數(shù),注意:如果使用new[]申請連續(xù)的空間是,該類必須提供無參或全缺省的構(gòu)造函數(shù) 3、malloc/free,new/delete,new[]/delete[]必須成對使用,否則會內(nèi)存泄漏或者代碼崩潰 */
注意:申請和釋放單個元素的空間,使用new和delete操作符,申請和釋放連續(xù)的空間,使用new[]和delete[]
四、實現(xiàn)原理
new的原理
1、調(diào)用operator new函數(shù)申請空間
2、在申請的空間上執(zhí)行構(gòu)造函數(shù),完成對象的構(gòu)造
delete的原理
1、在空間上執(zhí)行析構(gòu)函數(shù),完成對象中資源的清理工作
2、 調(diào)用operator delete函數(shù)釋放對象的空間
new T[N]的原理
1、調(diào)用operator new[]函數(shù),在operator new[]中實際調(diào)用operator new函數(shù)完成N個對象空間的申
請
2、在申請的空間上執(zhí)行N次構(gòu)造函數(shù)
delete[]的原理
1、 在釋放的對象空間上執(zhí)行N次析構(gòu)函數(shù),完成N個對象中資源的清理
2、調(diào)用operator delete[]釋放空間,實際在operator delete[]中調(diào)用operator delete來釋放空間
五、面試常問問題
1、malloc/free和new/delete的區(qū)別
共同點是:
都是從堆上申請空間,并且需要用戶手動釋放。
不同的地方是:
- malloc和free是函數(shù),new和delete是操作符
- malloc申請的空間不會初始化,new可以初始化
- malloc申請空間時,需要手動計算空間大小并傳遞,new只需在其后跟上空間的類型即可
- malloc的返回值為void*, 在使用時必須強轉(zhuǎn),new不需要,因為new后跟的是空間的類型
- malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲異常
- 申請自定義類型對象時,malloc/free只會開辟空間,不會調(diào)用構(gòu)造函數(shù)與析構(gòu)函數(shù),而new在申請空間后會調(diào)用構(gòu)造函數(shù)完成對象的初始化,delete在釋放空間前會調(diào)用析構(gòu)函數(shù)完成空間中資源的清理
2、內(nèi)存泄漏
什么是內(nèi)存泄漏:內(nèi)存泄漏指因為疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏并不是指內(nèi)存在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,因為設(shè)計錯誤,失去了對該段內(nèi)存的控制,因而造成了內(nèi)存的浪費。
內(nèi)存泄漏的危害:長期運行的程序出現(xiàn)內(nèi)存泄漏,影響很大,如操作系統(tǒng)、后臺服務(wù)等等,出現(xiàn)內(nèi)存泄漏會導(dǎo)致響應(yīng)越來越慢,最終卡死
內(nèi)存泄漏分類(了解)
C/C++程序中一般我們關(guān)心兩種方面的內(nèi)存泄漏:
1、堆內(nèi)存泄漏(Heap leak)
堆內(nèi)存指的是程序執(zhí)行中依據(jù)須要分配通過malloc / calloc / realloc / new等從堆中分配的一塊內(nèi)存,用完后必須通過調(diào)用相應(yīng)的 free或者delete 刪掉。假設(shè)程序的設(shè)計錯誤導(dǎo)致這部分內(nèi)存沒有被釋放,那么以后這部分空間將無法再被使用,就會產(chǎn)生Heap Leak。
2、系統(tǒng)資源泄漏
指程序使用系統(tǒng)分配的資源,比方套接字、文件描述符、管道等沒有使用對應(yīng)的函數(shù)釋放掉,導(dǎo)致系統(tǒng)資源的浪費,嚴(yán)重可導(dǎo)致系統(tǒng)效能減少,系統(tǒng)執(zhí)行不穩(wěn)定。
如何避免內(nèi)存泄漏
1、工程前期良好的設(shè)計規(guī)范,養(yǎng)成良好的編碼規(guī)范,申請的內(nèi)存空間記著匹配的去釋放。ps:這個理想狀態(tài)。但是如果碰上異常時,就算注意釋放了,還是可能會出問題。需要下一條智能指針來管理才有保證。
2、 采用RAII思想或者智能指針來管理資源。
3、有些公司內(nèi)部規(guī)范使用內(nèi)部實現(xiàn)的私有內(nèi)存管理庫。這套庫自帶內(nèi)存泄漏檢測的功能選項。
4、出問題了使用內(nèi)存泄漏工具檢測。ps:不過很多工具都不夠靠譜,或者收費昂貴。
解決方案:
1、事前預(yù)防型。如智能指針等。
2、事后查錯型。如泄漏檢測工具。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C語言數(shù)據(jù)類型枚舉enum全面詳解示例教程
生活中有很多地方會用到枚舉,比如一周有7天,可以一一枚舉;性別有男、女...等等都可以可以一一枚舉,今天來和筆者一起學(xué)習(xí)一下c語言枚舉吧2021-10-10C++實現(xiàn)LeetCode(23.合并k個有序鏈表)
這篇文章主要介紹了C++實現(xiàn)LeetCode(23.合并k個有序鏈表),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07