C++圖文并茂分析講解內(nèi)存管理
1.了解一些基本的內(nèi)存段(圖演示)
驗證棧是向下生長的
#include<iostream> using namespace std; int main() { int a = 3; int b = 4; int c = 5; int d = 6; cout <<"a:"<< &a << endl; cout << "b:"<<&b << endl; cout << "c:"<<&c << endl; cout << "d:"<<&d << endl; return 0; }
驗證堆一般是向上生長的(不一定)
#include<iostream> using namespace std; int main() { int num = 10; while (num--) { int *p1 = (int*)malloc(sizeof(int)); int *p2 = (int*)malloc(sizeof(int)); cout <<"p1"<< p1 << endl; cout <<"p2"<<p2 << endl; cout << endl; free(p1); } return 0; }
一般情況下,p1
的地址是比p2
的地址高的(因為堆一般是向上生長的),但是有時候是不一定的。
鞏固內(nèi)存管理知識點
答案
溫馨提示:題目中的指針是局部指針變量,是在棧上的,但是它指向的內(nèi)容(解引用)可能是堆區(qū)或者常量區(qū)的。,可以畫畫圖理解理解
2.c++申請動態(tài)內(nèi)存的新玩兒法new,delete
回顧c語言動態(tài)內(nèi)存管理的方式
malloc
和calloc
和realloc
malloc
堆上動態(tài)開空間calloc
堆上動態(tài)開空間+初始化成0等價于malloc
+memset
realloc
指針已有的空間擴容
原題增容–后面又足夠的空間
異地增容–后面沒有足夠的空間
開辟內(nèi)置類型的空間
//C++開辟動態(tài)內(nèi)存的新玩法 //語法演示: #include<iostream> using namespace std; int main() { //申請一個int的動態(tài)空間 int* p1 = (int*)malloc(sizeof(int)); *p1 = 1; int* p2 = new int(2);//這里是初始化 free(p1); delete p2; //申請一個10各int的動態(tài)數(shù)組 int *p3 = (int*)malloc(sizeof(int)* 10); for (int i = 0; i < 10; i++) { p3[i] = i + 1; } int *p4 = new int[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};//初始化 free(p3); delete[]p4; return 0; }
跟c語言的版本相比,c++的版本,初始化時顯得比較方便,而且語法比較簡潔。當(dāng)然了,更大的優(yōu)越性還在后面。
開辟自定義類型的空間(請用vs2013以上版本測試代碼)
#include<iostream> using namespace std; struct ListNode { int _val; ListNode* _next; ListNode* _prev; //構(gòu)造函數(shù) ListNode(int val = 0) :_val(val) , _next(nullptr) , _prev(nullptr) { cout<<"ListNode(int val = 0)"<<endl; } ~ListNode() { cout << "ListNode()" << endl; } }; int main() { //申請一個結(jié)點的空間 ListNode* pa = (ListNode*)malloc(sizeof(ListNode)*4); ListNode* pb = new ListNode(1);//不用去用sizeof去計算空間大小,很方便 free(pa); delete pb; //申請4個結(jié)點的空間--當(dāng)然了一般不會這么玩兒,我們只是看看效果 ListNode* pc = (ListNode*)malloc(sizeof(ListNode)* 4); ListNode* pd = new ListNode[4]{1, 2, 3, 4}; free(pc); delete[]pd; return 0; }
? 學(xué)過c語言版本的數(shù)據(jù)結(jié)構(gòu)的小伙伴都知道,我們push_back一個結(jié)點時,需要先實現(xiàn)一個buynewnode
的函數(shù)(創(chuàng)建一個結(jié)點,并且對其進行初始化)。而new這個操作符,在創(chuàng)建結(jié)點的同時,已經(jīng)幫我們實現(xiàn)了結(jié)點的初始化。調(diào)用了構(gòu)造函數(shù),而且delete
還調(diào)用了析構(gòu)函數(shù)。
總結(jié)一下
- c++中,如果是申請內(nèi)置類型對象或者數(shù)組,
malloc
和new
沒有太大區(qū)別 - 如果時自定義類型,區(qū)別很大,new和delete時開空間+初始化,析構(gòu)清理+釋放空間,
malloc
和free
僅僅時開空間+釋放空間 - 建議在c++中,無論時內(nèi)置類型還是自定義類型的申請釋放,盡量使用new和delete。
3. 32位平臺下可以開辟多大的內(nèi)存
我:cpu過來遭罪
cpu:你不要過來啊
上述程序,我們開了1.8 G左右的內(nèi)存,加上需要堆上的小內(nèi)存,最后的綜合有2 G的空間
如何開辟4G的內(nèi)存
將項目屬性修改一下,改成64位平臺即可,64位平臺有2^34 G的空間(虛擬內(nèi)存)在這我們不做細(xì)說,因為我也不太了解Linux。
4.了解operator new和operator delete
new
其實是operator new
+ 構(gòu)造函數(shù)
delete
其實是operator delete
+構(gòu)造函數(shù)
new
和delete
是用戶進行動態(tài)內(nèi)存申請和釋放的操作符,operator new
和operator delete
是系統(tǒng)提供的全局函數(shù),new在底層調(diào)用operator new全局函數(shù)來申請空間,delete在底層通過operator delete全局函數(shù)來釋放空間
大家可以將operator new和operator delete理解成和malloc
和free一樣用法的函數(shù)。
唯一不同的地方是,operator new和malloc
開辟空間失敗的處理方式不一樣,malloc
是返回NULL空指針,而operator是拋異常,下面代碼讓大家看看效果,不必深究,以后會有介紹。
struct ListNode { int _val; ListNode* _next; ListNode* _prev; //構(gòu)造函數(shù) ListNode(int val = 0) :_val(val) , _next(nullptr) , _prev(nullptr) { cout << "ListNode(int val = 0)" << endl; } ~ListNode() { cout << "ListNode()" << endl; } }; void f() { // 他的用法跟malloc和free是完全一樣的,功能都是在堆上申請釋放空間 // 失敗了處理方式不一樣,malloc失敗返回NULL,operator new失敗以后拋異常 ListNode* p1 = (ListNode*)malloc(sizeof(ListNode)); free(p1); ListNode* p2 = (ListNode*)operator new(sizeof(ListNode)); operator delete(p2); void* p3 = malloc(0x7fffffff); if (p3 == NULL) { cout << "malloc fail" << endl; } void* p4 = operator new(0x7fffffff); ListNode* p5 = new ListNode(2); cout << "繼續(xù)" << endl; } // // int main() { try { f(); } catch (exception& e) { cout << e.what() << endl; } return 0; }
malloc
失敗返回NULL,程序還會繼續(xù)向下執(zhí)行,但是operator new失敗,就會報異常,f函數(shù)后面的沒有在繼續(xù)執(zhí)行,然后走進catch語句中。
5.operator new和operator delete的類函數(shù)重載
struct ListNode//目的是提高效率 { ListNode* _next; ListNode* _prev; int _val; // 類中重載專屬operator new void* operator new(size_t n) { void* p = nullptr; p = allocator<ListNode>().allocate(1); cout << "memory pool allocate" << endl; return p; } void operator delete(void* p) { allocator<ListNode>().deallocate((ListNode*)p, 1); cout << "memory pool deallocate" << endl; } ListNode(int val) :_next(nullptr) , _prev(nullptr) , _val(val) {} }; int main() { ListNode* p = new ListNode(1); delete p; return 0; }
如果你自己在類里面寫了operator new
和operator delete
,那么編譯器就不會去調(diào)用系統(tǒng)提供的了,這是一種提高效率的方式。
我們是可以通過反匯編來看效果的
6.定位new–placement-new
通過上述的學(xué)習(xí)我們知道,malloc
,和operator
是不會去調(diào)用構(gòu)造函數(shù)的,new
會去調(diào)用構(gòu)造函數(shù),而且構(gòu)造函數(shù)是只允許構(gòu)造出對象時調(diào)用,而你的對象創(chuàng)建出來之后是無法調(diào)用構(gòu)造的。
但是如果我們operator new
了一塊空間(未初始化),但是又想要調(diào)用其構(gòu)造函數(shù),該怎們辦呢?
class A { public: A(int a = 0) :_a(a) { cout << "A(int a = 0)" << endl; } ~A() { cout << "~A()" << endl; } private: int _a; }; int main() { A* pa = (A*)operator new(sizeof(A)); //pa->A();//error錯誤調(diào)用方式,構(gòu)造函數(shù)只允許構(gòu)造時進行調(diào)用 new(pa)A; // 定位new,placement-new,顯示調(diào)用構(gòu)造函數(shù)初始化這塊對象空間 A* pb = (A*)operator new(sizeof(A)); new(pb)A(3); A* pc = new A; new(pc)A(3); // 等于 delete p pa->~A(); // 析構(gòu)函數(shù)可以顯示調(diào)用 operator delete(pa); pb->~A(); operator delete(pb); delete pc; return 0; }
大家要知道定位new哦,他是一種對已經(jīng)創(chuàng)建出來的對象,還能繼續(xù)調(diào)用其構(gòu)造函數(shù)的方式。
7.`malloc`/`free`和`new`/`delete`的區(qū)別
??济嬖囶}
8.再次理解內(nèi)存泄漏
首先大家先想一想這個問題:內(nèi)存泄漏是指針丟了還是內(nèi)存丟了?
內(nèi)存管理中,內(nèi)存是用指針去維護的,當(dāng)你的動態(tài)內(nèi)存空間還沒有釋放時,你已經(jīng)把指針弄丟了,那么你將無法控制這段空間,進而導(dǎo)致內(nèi)存泄漏。
什么是內(nèi)存泄漏,內(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)越來越慢,最終卡死。
到此這篇關(guān)于C++圖文并茂分析講解內(nèi)存管理的文章就介紹到這了,更多相關(guān)C++內(nèi)存管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++ 多態(tài)性虛函數(shù)和動態(tài)綁定學(xué)習(xí)筆記
- 關(guān)于C++虛函數(shù)與靜態(tài)、動態(tài)綁定的問題
- C++實現(xiàn)動態(tài)綁定代碼分享
- 深入理解C++的動態(tài)綁定與靜態(tài)綁定的應(yīng)用詳解
- 詳解C++動態(tài)內(nèi)存管理
- C++內(nèi)存管理面經(jīng)
- 詳解C++中動態(tài)內(nèi)存管理和泛型編程
- 一文詳解C++中動態(tài)內(nèi)存管理
- C語言與C++中內(nèi)存管理詳解
- 一起來學(xué)習(xí)C++的動態(tài)內(nèi)存管理
- C++中動態(tài)綁定和內(nèi)存管理的實現(xiàn)
相關(guān)文章
C語言 數(shù)據(jù)結(jié)構(gòu)雙向鏈表簡單實例
這篇文章主要介紹了C語言 數(shù)據(jù)結(jié)構(gòu)雙向鏈表簡單實例的相關(guān)資料,需要的朋友可以參考下2017-03-03C 程序?qū)崿F(xiàn)密碼隱秘輸入的實例 linux系統(tǒng)可執(zhí)行
下面小編就為大家?guī)硪黄狢 程序?qū)崿F(xiàn)密碼隱秘輸入的實例 linux系統(tǒng)可執(zhí)行。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11GCC 編譯使用動態(tài)鏈接庫和靜態(tài)鏈接庫的方法
根據(jù)鏈接時期的不同,庫又有靜態(tài)庫和動態(tài)庫之分,有別于靜態(tài)庫,動態(tài)庫的鏈接是在程序執(zhí)行的時候被鏈接的2013-03-03C語言中的結(jié)構(gòu)體的入門學(xué)習(xí)教程
這篇文章主要介紹了C語言中的結(jié)構(gòu)體的入門學(xué)習(xí)教程,以struct語句定義的結(jié)構(gòu)體是C語言編程中的重要基礎(chǔ),需要的朋友可以參考下2015-12-12