網站首頁 編程語言 正文
C/C++內存分配方式
在學習C語言階段的時候,創建一個變量,編譯器會為它分配一塊內存。而創建一個C++對象的時候,編譯器會為這個對象分配內存,并且調用合適的構造函數進行初始化。
那么編譯器的內存分配方式是怎樣的呢?
內存分配可以有以下的幾種方式
- 從靜態存儲區分配。這樣的分配方式在程序開始前就可以為對象/變量分配,這塊空間在整個程序運行期間都存在。
- 從棧區分配。調用函數時,函數的參數,局部變量,返回地址等存儲在堆棧上,函數執行結束時將會自動釋放這些內存空間,棧區的內存空間遠遠小于堆區。
- 從堆區分配。這種內存分配方式被稱為動態內存分配,堆區又被稱為“自由存儲單元”,運行時通過調用相應的函數來申請和釋放內存。
有時候我們并不知道程序中的對象確切地需要多少內存空間,動態內存分配則很好地處理了這種需求。
C++內存管理方式
C庫中提供了函數malloc,以及它的變種函數realloc、calloc來動態地申請內存空間。使用函數free來釋放動態申請出的內存空間。
int* ptr1 = (int*)malloc(sizeof(int));
使用malloc需要指定空間大小,并且要強制類型轉化,因為它只是簡單地分配了一塊空間,返回的是void*,而C++中不允許將空類型的指針賦予給其他類型的指針。另外,如果你申請一塊內存之后,沒有對這個指針進行正確的初始化,有可能會導致程序運行失敗,并且如果忘記釋放動態申請的內存空間,則會造成內存泄露等危害……
在創建一個C++對象時,編譯器會做這兩件事:
1.為對象分配內存。
2.編譯器自動調用構造函數初始化該內存。
構造函數不支持顯式地調用,意味著如果使用malloc函數創建一個對象,那么這個對象將不能夠調用構造函數,僅僅只是開辟了一塊空間。但是我們必須要確保對象被初始化,因為未初始化對象是大部分程序出錯的主要原因。總而言之,C中的動態內存管理無法滿足C++中動態對象的需求。
所以提出了new和delete.關鍵字
new和delete的使用
int main(void) { //基本內置類型 //開辟一個int類型的空間 int* ptr2 = new int; //開辟多個int類型空間 int* ptr3 = new int[5]; //開辟一個int類型并初始化為1 int* ptr4 = new int(1); delete ptr2; delete[] ptr3; delete ptr4; return 0; }
new和delete的使用方式:
1.開辟一個空間: new 類型; 對應釋放: delete 對象;2.開辟多個空間:new 類型[個數] 對應釋放:delete[] 對象;3.開辟并初始化: new 類型(初始化數據) 對應釋放:delete 對象 1.開辟一個空間: new 類型; 對應釋放: delete 對象; 2.開辟多個空間:new 類型[個數] 對應釋放:delete[] 對象; 3.開辟并初始化: new 類型(初始化數據) 對應釋放:delete 對象
new和delete的騷操作
內置類型
對于內置類型,new和delete與C的內存管理函數做了差不多的事情,不同的地方是:new/delete申請和釋放的是單個元素的空間,new[]和delete[]申請的是連續空間,而且new在申請空間失敗時會拋異常,malloc會返回NULL。
自定義類型
new表達式:
- 調用opreator new函數分配內存
- 調用構造函數初始化該內存
delete表達式:
- 調用析構函數清理對象中的資源
- 調用operator delete釋放空間。
operator new( ) 和operator delete( )這兩個內存分配函數是系統提供的全局函數,實際上是對malloc和free的各種行為進行了封裝。
new和delete的區別
new、delete 和 malloc、free的區別有哪些呢?
- new和delete是關鍵字,malloc和free是函數
- malloc申請的空間不會初始化,new會初始化。
- malloc需要手動計算空間大小并傳遞,new不需要
- malloc的返回值是void*,使用時必須強制類型轉換;new不需要,后面跟的是空間的類型。
- malloc申請失敗,返回NULL,所以調用后要判斷是否開辟成功;new需要捕獲異常
- malloc只是開辟空間,不會調用構造函數,free釋放空間不會調用析構函數;new在申請空間后會調用構造函數初始化對允許象,delete會調用析構函數清理對象中的資源,然后再釋放空間。
重載new和delete
C++允許重載new和delete,以實現我們自己的存儲分配方案。但是注意重載operator new和operator delete時,僅僅只能改變原本的內存分配方式。同重載其他的運算符一樣,可以分為重載成全局和針對特定類的內存分配函數。
重載全局
重載一個全局的new和delete會導致默認版本完全不能被訪問。
重載operator new的要求:
- 必須有一個size_t參數,該參數將接收要開辟空間的長度。
- 返回一個指向對象的指針,該對象的長度等于或者大于所申請的長度。
- 如果分配失敗,不僅僅要返回一個0,還需產生一個異常信息之類的現象,明確分配內存時出了問題。
- 返回值是一個void*
重載operator delete的要求
- 參數是一個指向由operator new()分配的void*類型的內存的指針
- 返回值是void
為什么重載operator delete的時候,參數是一個void*?
這是因為它是在調用析構函數后得到的指針。
//重載operator new //1.必須有一個size_t的參數,該參數將接收要申請開辟空間的大小 //2.返回值是一個void* //3.返回一個指向對象的指針,該對象的長度等于或大于所申請的長度 //4.如果分配失敗。不僅僅要返回一個0,還需產生一個異常信息 void* operator new(size_t sz) { cout << "new %d Bytes" << sz << endl; void* p = nullptr; //不需要強制類型轉換,因為malloc返回的就是void* p = malloc(sz); if (nullptr == p) { cout << "new fail\n" << endl; } return p; } //重載operator delete //1.返回值為void //2.參數是一個指向由operator new()返回的void*的指針 void operator delete(void* rp) { cout << "operator delete" << endl; free(rp); }
重載類專屬
//重載ListNode專屬的operator new struct ListNode { ListNode* _next; ListNode* _prev; int _data; 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; } }; class List { public: List() { _head = new ListNode; _head->_next = _head; _head->_prev = _head; } ~List() { ListNode* cur = _head->_next; while (cur != _head) { ListNode* next = cur->_next; delete cur; cur = next; } delete _head; _head = nullptr; } private: ListNode* _head; }; int main() { List l; return 0; }
定位new表達式
定位new表達式:它的作用是在已分配的原始內存空間中用構造函數初始化一個對象
使用的格式:
new (place_address) type 或者 new (place_address) type (initializer-lost) place_address
必須是一個指針,initializer-list
是初始化列表。
//例如 class Test { public: Test() : _data(0) { cout << "Test():" << this << endl; } ~Test() { cout << "~Test():" << this << endl; } private: int _data; }; int main(void) { // pt現在指向的只不過是與Test對象相同大小的一段空間,還不能算是一個對象,因為構造函數沒有執行 Test* pt = (Test*)malloc(sizeof(Test)); //定位new new(pt) Test; // 注意:如果Test類的構造函數有參數時,此處需要傳參 return 0; }
內存泄露
內存泄露的含義:
內存泄漏指因為疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏并不是指內存在物理上的消失,而是應用程序分配某段內存后,因為設計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。
內存泄露的兩大分類:
1.堆內存泄露(Heap leak)
堆內存指的是程序執行中依據須要分配通過malloc / calloc / realloc / new等從堆中分配的一塊內存,用完后必須通過調用相應的 free或delete 刪掉。假設程序的設計錯誤導致這部分內存沒有被釋放,那么以后這部分空間將無法再被使用,就會產生Heap Leak。
2.系統資源泄露
指程序使用系統分配的資源,比方套接字、文件描述符、管道等沒有使用對應的函數釋放掉,導致系統資源的浪費,嚴重可導致系統效能減少,系統執行不穩定。
內存泄露的危害:
在平時寫一些小測試的時候,并沒有覺得內存泄露的危害特別大,但是在長期運行的程序中出現內存泄漏,影響非常的大,出現內存泄露可能會導致響應越來越慢,最終出現卡死的現象。
內存泄露的解決方案分兩種:
1.事先預防 。
2. 事后查錯
如何事先預防?
1.養成良好的編碼習慣,申請了內存要記得釋放。
2.采用RAII思想或者智能指針來管理資源。
3.規范使用內部實現的私有內存管理庫。
總結
原文鏈接:https://blog.csdn.net/qq_56870066/article/details/123762062
相關推薦
- 2021-12-07 bitbucket搭建詳細過程記錄_其它綜合
- 2022-09-10 C#中AutoResetEvent控制線程用法小結_C#教程
- 2022-06-27 python?使用ctypes調用C/C++?dll詳情_python
- 2022-06-16 C語言從猜數字游戲中理解數據結構_C 語言
- 2022-07-11 Jenkins修改默認主目錄
- 2022-07-24 .Net行為型設計模式之中介者模式(Mediator)_基礎應用
- 2022-04-22 Element UI 使用表單校驗,正確輸入后,仍然有提示信息
- 2022-11-05 Swift?Access?Control訪問控制與斷言詳細介紹_Swift
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支