<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在我們日常寫程式碼的過程中,我們對記憶體空間的需求有時候在程式執行的時候才能知道,這時候我們就需要使用動態開闢記憶體的方法。
首先我們先了解一下C/C++程式記憶體分配的幾個區域:
int globalVar = 1; static int staticGlobalVar = 1; void Test() { static int staticVar = 1; int localVar = 1; int num1[10] = { 1, 2, 3, 4 }; char char2[] = "abcd"; const char* pChar3 = "abcd"; 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); }
這幅圖中,我們可以發現普通的區域性變數是在棧上分配空間的,在棧區中建立的變數出了作用域去就會自動銷燬。但是被static修飾的變數是存放在資料段(靜態區),在資料段上建立的變數直到程式結束才銷燬,所以資料段上的資料生命週期變長了。
在C語言中,我們經常會用到malloc,calloc和realloc來進行動態的開闢記憶體;同時,C語言還提供了一個函數free,專門用來做動態記憶體的釋放和回收。其中他們三個的區別也是我們需要特別所強調區別的。
malloc函數是向記憶體申請一塊連續可用的空間,並返回指向這塊空間的指標。
calloc與malloc的區別只在於calloc會在返回地址之前把申請的空間的每個位元組初始化為0。
realloc函數可以做到對動態開闢記憶體大小的調整。
我們通過這三個函數的定義也可以進行功能的區分:
void Test () { int* p1 = (int*) malloc(sizeof(int)); free(p1); int* p2 = (int*)calloc(4, sizeof (int)); int* p3 = (int*)realloc(p2, sizeof(int)*10); free(p3 ); }
我們都知道,C++語言是相容C語言的,因此C語言中記憶體管理方式在C++中可以繼續使用。但是有些地方就無能為力了,並且使用起來也可能比較麻煩。因此,C++擁有自己的內管管理方式:通過new和delete操作符進行動態記憶體管理。
int main() { // 動態申請一個int型別的空間 int* ptr1 = new int; // 動態申請一個int型別的空間並初始化為10 int* ptr2 = new int(10); // 動態申請3個int型別的空間(陣列) int* ptr3 = new int[3]; // 動態申請3個int型別的空間,初始化第一個空間值為1 int* ptr4 = new int[3]{ 1 }; delete ptr1; delete ptr2; delete[] ptr3; delete[] ptr4; return 0; }
我們首先通過畫圖分析進行剖析程式碼:
我們在監視視窗看看這3個變數
注意:申請和釋放單個元素的空間,使用new和delete操作符,申請和釋放連續的空間,使用new[]和delete[],要匹配起來使用。
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; return 0; }
在這段程式碼中,p1是我們使用malloc開闢的,p2是通過new來開闢的。我們編譯執行這段程式碼。
發現輸出了這兩句,那這兩句是誰呼叫的呢?我們通過偵錯逐語句來分析這個過程
內建型別區別
注意:在申請自定義型別的空間時,new會自動呼叫建構函式,delete時會呼叫解構函式,而malloc和free不會。
int main() { void* p0 = malloc(1024 * 1024 * 1024); cout << p0 << endl; //malloc失敗,返回空指標 void* p1 = malloc(1024 * 1024 * 1024); cout << p1 << endl; try { //new失敗,拋異常 void* p2 = new char[1024 * 1024 * 1024]; cout << p2 << endl; } catch (const exception& e) { cout << e.what() << endl; } return 0; }
我們能夠發現,malloc失敗時會返回空指標,而new失敗時,會丟擲異常。
C++標準庫還提供了operator new和operator delete函數,但是這兩個函數並不是對new和delete的過載,operator new和operator delete是兩個庫函數。(這裡C++大佬設計時這樣取名確實很容易混淆)
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { // try to allocate size bytes void* p; 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失敗,丟擲異常。
該函數最終是通過free來釋放空間的
//operator delete 原始碼 void operator delete(void* pUserData) { _CrtMemBlockHeader* pHead; RTCCALLBACK(_RTC_Free_hook, (pUserData, 0)); if (pUserData == NULL) return; _mlock(_HEAP_LOCK); /* block other threads */ __TRY /* get a pointer to memory block header */ pHead = pHdr(pUserData); /* verify block type */ _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); _free_dbg(pUserData, pHead->nBlockUse); __FINALLY _munlock(_HEAP_LOCK); /* release other threads */ __END_TRY_FINALLY return; } /* free的實現 */ #define free(p) _free_dbg(p, _NORMAL_BLOCK)
class A { public: A(int a = 0) : _a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; int main() { //跟malloc功能一樣,失敗以後丟擲異常 A* ps1 = (A*)operator new(sizeof(A)); operator delete(ps1); A* ps2 = (A*)malloc(sizeof(A)); free(ps2); A* ps3 = new A; delete ps3; return 0; }
我們使用new的時候,new要開空間,要呼叫建構函式。new可以轉換成call malloc,call 建構函式。但是call malloc 一旦失敗,會返回空指標或者錯誤碼。在物件導向的語言中更喜歡使用異常。而operator new相比較malloc的不同就在於如果一旦失敗會丟擲異常,因此new的底層實現是呼叫operator new,operator new會呼叫malloc(如果失敗丟擲異常),再呼叫建構函式。
我們通過組合看一下ps3
operator delete同理。
總結:通過上述兩個全域性函數的實現知道,operator new 實際也是通過malloc來申請空間,如果malloc申請空間成功就直接返回,否則執行使用者提供的空間不足應對措施,如果使用者提供該措施就繼續申請,否則就拋異常。operator delete 最終是通過free來釋放空間的。
專屬的operator new技術,提高效率。應用:記憶體池
class A { public: A(int a = 0) : _a(a) { cout << "A():" << this << endl; } // 專屬的operator new void* operator new(size_t n) { void* p = nullptr; p = allocator<A>().allocate(1); cout << "memory pool allocate" << endl; return p; } void operator delete(void* p) { allocator<A>().deallocate((A*)p, 1); cout << "memory pool deallocate" << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; int main() { int n = 0; cin >> n; for (int i = 0; i < n; ++i) { A* ps1 = new A; //operator new + A的建構函式 } return 0; }
注意:一般情況下不需要對 operator new 和 operator delete進行過載,除非在申請和釋放空間時候有某些特殊的需求。比如:在使用new和delete申請和釋放空間時,列印一些紀錄檔資訊,可以簡單幫助使用者來檢測是否存在記憶體漏失。
如果申請的是內建型別的空間,new和malloc,delete和free基本類似,不同的地方是:new/delete申請和釋放的是單個元素的空間,new[]和delete[]申請的是連續空間,而且new在申請空間失敗時會拋異常,malloc會返回NULL。
都是從堆上申請空間,都需要使用者手動釋放空間。
到此這篇關於一文詳解C++中動態記憶體管理的文章就介紹到這了,更多相關C++動態記憶體管理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45