優化C++資源利用:探索高效內存管理技巧

W...Y的主頁 😊

代碼倉庫分享💕?


🍔前言:
我們之前在C語言中學習過動態內存開辟,使用malloc、calloc與realloc進行開辟,使用free進行堆上內存的釋放。進入C++后對于動態內存開辟我們又有了新的內容new與delete。今天我們來學習C++中的動態內存開辟!

我們先來進行一下內存管理的復習。

目錄

C/C++內存分布

C語言中動態內存管理方式:malloc/calloc/realloc/free?

C++內存管理方式

new/delete操作內置類型

new和delete操作自定義類型

operator new與operator delete函數

new和delete的實現原理

內置類型

?自定義類型

定位new表達式(placement-new)

C++與new的使用場景?


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);
}
1. 選擇題:選項: A.棧 ?B.堆 ?C.數據段(靜態區) ?D.代碼段(常量區)globalVar在哪里?____ ?staticGlobalVar在哪里?____staticVar在哪里?____ ?localVar在哪里?____num1 在哪里?____char2在哪里?____ ?*char2在哪里?___pChar3在哪里?____ ???*pChar3在哪里?____ptr1在哪里?____ ????*ptr1在哪里?____

?globalVar在哪里?C? staticGlobalVar在哪里?C??staticVar在哪里?C? localVar在哪里?A??num1 在哪里?A??char2在哪里?A? *char2在哪里?A? pChar3在哪里?A? ?*pChar3在哪里?D??ptr1在哪里?A? ?*ptr1在哪里?C

這些都是上述的答案,全部是關于各種類型的數據在C++中的存放位置。

【說明】
1. 棧又叫堆棧--非靜態局部變量/函數參數/返回值等等,棧是向下增長的。
2. 內存映射段是高效的I/O映射方式,用于裝載一個共享的動態內存庫。用戶可使用系統接口
創建共享共享內存,做進程間通信。(Linux課程如果沒學到這塊,現在只需要了解一下)
3. 堆用于程序運行時動態內存分配,堆是可以上增長的。
4. 數據段--存儲全局數據和靜態數據。
5. 代碼段--可執行的代碼/只讀常量。

C語言中動態內存管理方式:malloc/calloc/realloc/free?

void Test ()
{
int* p1 = (int*) malloc(sizeof(int));
free(p1);
// 1.malloc/calloc/realloc的區別是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 這里需要free(p2)嗎?
free(p3 );
}

相信大家對malloc與calloc非常熟悉,唯一的區別就是參數不同,還有就是calloc給予開辟空間初始化,而malloc卻沒有。realloc是對calloc與malloc進行擴容的,擴容分為異地擴容與原地擴容,當目標位置空間足夠時會進行原地擴容,反之如果不夠將進行異地擴容。

博主在之前的博客中詳細講解了C語言中的動態內存開辟,如果有疑問可以點擊下面鏈接進行學習:C語言中動態內存管理方式:malloc/calloc/realloc/free?icon-default.png?t=N7T8https://blog.csdn.net/m0_74755811/article/details/131820896?spm=1001.2014.3001.5501

C++內存管理方式

C語言內存管理方式在C++中可以繼續使用,但有些地方就無能為力,而且使用起來比較麻煩,因
此C++又提出了自己的內存管理方式:通過new和delete操作符進行動態內存管理。?

new/delete操作內置類型

void Test()
{// 動態申請一個int類型的空間int* ptr4 = new int;// 動態申請一個int類型的空間并初始化為10int* ptr5 = new int(10);// 動態申請10個int類型的空間int* ptr6 = new int[3];delete ptr4;delete ptr5;delete[] ptr6;
}

通過上述代碼可以看出,使用new進行申請空間非常簡單,只需要new加上類型即可。如果我們想進行初始化即可在后面加上(n)即可。當進行開辟多個內存空間時,我們像申請數組一樣進行申請即可。但是在多個內存釋放時,一定要加上[]。

看到現在我們覺得malloc與new的功能差不多呀,最多就是少些一些字母,那為什么C++還要創造一個新的字符進行學習呢?我們接著往下看:

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()
{
// new/delete 和 malloc/free最大區別是 new/delete對于【自定義類型】除了開空間
//還會調用構造函數和析構函數
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(1);
free(p1);
delete p2;
// 內置類型是幾乎是一樣的
int* p3 = (int*)malloc(sizeof(int)); // C
int* p4 = new int;
free(p3);
delete p4;
A* p5 = (A*)malloc(sizeof(A)*10);
A* p6 = new A[10];
free(p5);
delete[] p6;
return 0;
}

?在內置類型中new與malloc是沒有任何區別的,而在自定義類型中就會體現出極大的不同。在自定義類型中malloc不會對開辟的成員對象進行初始化,而new會自動調用構造函數。而在結束時delete會調研析構函數。所以說new與delete關鍵字就是為C++面向對象而產生的!!!

operator new與operator delete函數

?new和delete是用戶進行動態內存申請和釋放的操作符,operator new 和operator delete是
系統提供的全局函數,new在底層調用operator new全局函數來申請空間,delete在底層通過
operator delete全局函數來釋放空間。

/*
operator new:該函數實際通過malloc來申請空間,當malloc申請空間成功時直接返回;申請空間
失敗,嘗試執行空 ???????間不足應對措施,如果改應對措施用戶設置了,則繼續申請,否
則拋異常。
*/
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 delete: 該函數最終是通過free來釋放空間的
*/
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_FINALLYreturn;
}
/*
free的實現
*/
#define ?free(p) ???????_free_dbg(p, _NORMAL_BLOCK)

通過上述兩個全局函數的實現知道,operator new 實際也是通過malloc來申請空間,如果
malloc申請空間成功就直接返回,否則執行用戶提供的空間不足應對措施,如果用戶提供該措施
就繼續申請,否則就拋異常。operator delete 最終是通過free來釋放空間的。

那我們就會有疑問,new的底層邏輯就是malloc,delete的底層邏輯就是free。那么我們使用new開辟的空間能不能使用free進行釋放呢?

答案是:可以,但最好不要交叉使用。因為有時候程序會正常進行,但是有時候就會報錯。這是為什么呢?

當我們使用new申請一塊非常簡單的空間,只有一些基本的變量,最多就是少調用了一層析構函數,并不會影響空間的釋放。但是當我們進行比如棧的開辟創建:

class Stack
{
public:Stack(){cout << "Stack()" << endl;_a = new int[4];_top = 0;_capacity = 4;}~Stack(){cout << "~Stack()" << endl;delete[] _a;_top = _capacity = 0;}private:int* _a;int _top;int _capacity;
};
int main()
{Stack st;Stack* pst = new Stack;delete pst;return 0;
}

第一種情況是指針指向在堆開好的空間,只有兩層關系,而使用new進行開辟先在堆上開辟對象空間,對象在使用構造函數進行初始化在堆上再開一層空間,是三層的關系。如果我們要使用free進行釋放空間,只能將第二層進行釋放,而第三層就產生了內存泄漏!!!

所以我們不要交叉使用,做到一一對應!!!

new和delete的實現原理

內置類型

如果申請的是內置類型的空間,new和malloc,delete和free基本類似,不同的地方是:
new/delete申請和釋放的是單個元素的空間,new[]和delete[]申請的是連續空間,而且new在申
請空間失敗時會拋異常,malloc會返回NULL。

?自定義類型

new的原理
1. 調用operator new函數申請空間
2. 在申請的空間上執行構造函數,完成對象的構造
delete的原理
1. 在空間上執行析構函數,完成對象中資源的清理工作
2. 調用operator delete函數釋放對象的空間
new T[N]的原理
1. 調用operator new[]函數,在operator new[]中實際調用operator new函數完成N個對
象空間的申請
2. 在申請的空間上執行N次構造函數
delete[]的原理
1. 在釋放的對象空間上執行N次析構函數,完成N個對象中資源的清理
2. 調用operator delete[]釋放空間,實際在operator delete[]中調用operator delete來釋
放空間?

定位new表達式(placement-new)

定位new表達式是在已分配的原始內存空間中調用構造函數初始化一個對象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必須是一個指針,initializer-list是類型的初始化列表
使用場景:
定位new表達式在實際中一般是配合內存池使用。因為內存池分配出的內存沒有初始化,所以如
果是自定義類型的對象,需要使用new的定義表達式進行顯示調構造函數進行初始化。

class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
// 定位new/replacement new
int main()
{
// p1現在指向的只不過是與A對象相同大小的一段空間,還不能算是一個對象,因為構造函數沒
有執行
A* p1 = (A*)malloc(sizeof(A));
new(p1)A; ?// 注意:如果A類的構造函數有參數時,此處需要傳參
p1->~A();
free(p1);
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);return 0;
}

C++與new的使用場景?

雖然malloc與new都是在堆上進行開辟空間,但是他們獲取內存的方式不一樣。malloc是需要多少就索取多少,而new是”提前預支“內存,這樣就可以提高new的效率,但是卻導致了new空間浪費。所以說有利有弊,我們應該在適當的情況使用適當的做法。

C++中使用malloc和new有不同的用途和行為,你可以根據需要選擇哪個更適合你的情況。以下是一些情況下的推薦用法:
使用malloc的情況:

1.C兼容性: 如果你編寫的是C++代碼,并且需要與C庫或其他C代碼進行交互,使用malloc可能更合適,因為malloc是C標準庫函數。
2.需要手動管理構造和析構: malloc只分配內存,不會自動調用構造函數或析構函數。如果你需要手動控制對象的構造和析構過程,或者分配的內存不是用于存儲對象(例如分配原始字節數組),則使用malloc。
3.需要明確指定內存大小: malloc接受一個字節數作為參數,而new會考慮類型的大小和額外的構造函數開銷。如果你需要確切控制內存分配的字節數,可以使用malloc。
4.不需要類型檢查: new是類型安全的,而malloc不是。如果你需要執行類型不安全的操作,可能需要使用malloc。

使用new的情況:

5.C++對象分配: 如果你需要分配內存以存儲C++對象,通常應使用new或new[]。new會自動調用對象的構造函數,new[]用于動態分配數組,并在必要時調用構造函數。
6.類型安全: new提供了類型安全性,可以避免一些常見的內存錯誤,例如內存泄漏和越界訪問。
7.更簡潔的語法: new和new[]的語法更簡潔,不需要顯式指定分配的字節數。
8.自動內存管理: new分配的內存會在對象的生命周期結束時自動釋放,從而減少了內存泄漏的風險。

總的來說,如果你編寫純粹的C++代碼并需要分配內存以存儲對象,通常建議使用new或new[],因為它們提供更好的類型安全性和內存管理。使用malloc通常是在需要更底層的內存分配控制,或者與C代碼進行交互時的情況。無論使用哪種方法,都需要謹慎管理內存,確保在不再需要時釋放它,以避免內存泄漏。


以上就是本次全部內容,感謝大家觀看!!!?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/164707.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/164707.shtml
英文地址,請注明出處:http://en.pswp.cn/news/164707.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CCC聯盟——UWB MAC(一)

本文在前面已經介紹了相關UWB的PHY之后&#xff0c;重點介紹數字鑰匙&#xff08;Digital Key&#xff09;中關于MAC層的相關實現規范。由于MAC層相應涉及內容比較多&#xff0c;本文首先從介紹UWB MAC的整體框架&#xff0c;后續陸續介紹相關的網絡、協議等內容。 1、UWB MAC架…

真心的表揚與鼓勵,勝過一萬句說教

今天我想和大家分享一下&#xff0c;怎樣跟孩子運用鼓勵和表揚。我記得魯道夫德雷克斯是阿德勒學派的心理學家&#xff0c;也是來自《孩子的挑戰》一書的作者&#xff0c;他說孩子們需要鼓勵&#xff0c;就像植物需要水&#xff0c;鼓勵能讓孩子知道自己做的事與自己是什么樣的…

非自定義Bean注解開發Bean配置類的注解開發

目錄 非自定義Bean注解開發 Bean配置類的注解開發 非自定義Bean注解開發 非自定義的Bean不能像自定義Bean使用Component進行管理&#xff0c;非自定義Bean要通過工廠的方式進行實例化&#xff0c;使用Bean標注方法即可&#xff0c;Bean的屬性文beanName 如果Bean工廠方法需要參…

[23] 4K4D: Real-Time 4D View Synthesis at 4K Resolution

paper | proj | code 提出一種基于K-Planes的4D point cloud Representation&#xff1b;提出一種Hybrid appearance model&#xff0c;包含image blending model和SH model。其中&#xff0c;image blending model將3D點映射回原圖中求得&#xff0c;SH model通過模型預測求得…

【工具欄】熱部署不生效

目錄 配置熱部署&#xff1a; 解決熱部署不生效&#xff1a; 首先檢查&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 配置熱部署&#xff1a; https://blog.csdn.net/m0_67930426/article/details/133690559 解決熱部署不…

Python中的解析器argparse

import argparse## 構造解析器 argparse.ArgumentParser() parse argparse.ArgumentParser(description"caculateing the area of rectangle")## 添加參數 .add_argument() parse.add_argument("--length",typeint,default20,helpThe length of rectangle…

【追求卓越09】算法--散列表(哈希表)

引導 通過前面幾個章節的學習&#xff08;二分查找&#xff0c;跳表&#xff09;&#xff0c;我們發現想要快速查找某一個元素&#xff0c;首先需要將所有元素進行排序&#xff0c;再利用二分法思想進行查找&#xff0c;復雜度是O(logn)。有沒有更快的查找方式呢&#xff1f; 本…

微軟發布最新.NET 8長期支持版本,云計算、AI應用支持再強化

11 月 15 日開始的為期三天的 .NET Conf 在線活動的開幕日上&#xff0c;.NET 8作為微軟的開源跨平臺開發平臺正式發布。.NET 團隊著重強調云、性能、全棧 Blazor、AI 和 .NET MAUI 是.NET 8的主要亮點。.NET團隊在 .NET Conf 2023 [1]活動開幕式上表示&#xff1a;“通過這個版…

nginx 模塊相關配置及結構理解

文章目錄 模塊配置結構模塊配置指令先看一下 ngx_command_t 結構一個模塊配置的demo簡單模塊配置的案例演示 模塊上下文結構模塊的定義 模塊配置結構 Nginx中每個模塊都會提供一些指令&#xff0c;以便于用戶通過配置去控制該模塊的行為。 Nginx的配置信息分成了幾個作用域(sc…

使用注解的AOP編程

使用注解的AOP編程 當注解沒有參數時 當使用注解進行面向切面編程&#xff08;AOP&#xff09;時&#xff0c;你可以按照以下步驟來實現&#xff1a; 步驟&#xff1a; 1. 創建自定義注解&#xff1a; 首先&#xff0c;創建自定義的注解&#xff0c;以便在代碼中標記需要進…

Excel換不了行怎么解決?

方法一: 使用Alt Enter鍵 在Excel中&#xff0c;輸入文字時按下回車鍵&#xff0c;光標將會移到下一個單元格&#xff0c;如果想要換行&#xff0c;可以嘗試使用Alt Enter鍵。具體操作如下: 1.在單元格中輸入文字; 2.想要換行時&#xff0c;在需要換行的位置按下Alt Enter鍵; 3…

延時任務定時發布,基于 Redis 與 DB 實現

目錄 1、什么是延時任務&#xff0c;分別可以使用哪些技術實現&#xff1f; 1.2 使用 Redis 和 DB 相結合的思路圖以及分析 2、實現添加任務、刪除任務、拉取任務 3、實現未來數據的定時更新 4、將數據庫中的任務數據&#xff0c;同步到 Redis 中 1、什么是延時任務&#xff…

網絡運維與網絡安全 學習筆記2023.11.23

網絡運維與網絡安全 學習筆記 第二十四天 今日目標 VRRP負載均衡、BFD原理與配置、BFD典型應用 DHCP工作原理、全局模式DHCP VRRP負載均衡 VRRP單組缺陷 每網段存在一個VRRP組&#xff0c;缺點如下&#xff1a; 主網關數據轉發壓力大 備份網關不轉發任何數據 網絡設備利用…

Hook技術(鉤子技術)

HOOK&#xff08;鉤子技術&#xff09; 這里的hook我理解的意思就是通過攔截指令&#xff0c;將指令換成自己想要的指令&#xff0c;從而做道繞過原本的程序指令&#xff0c;要修改這個指令&#xff0c;要用匯編技術&#xff0c;從二進制入手。 擴展&#xff1a; 木馬病毒之…

git clone慢的解決辦法

在網站 https://www.ipaddress.com/ 分別搜索&#xff1a; github.global.ssl.fastly.net github.com 得到ip&#xff1a; 打開hosts文件 sudo vim /etc/hosts 在hosts文件末尾添加 140.82.114.3 github.com 151.101.1.194 github.global-ssl.fastly.net 151.101.65.194 g…

外部網關協議_邊界網關協議BGP

一.邊界網關協議BGP的基本概念 邊界網關協議(Border Gateway Protocol&#xff0c;BGP&#xff09;屬于外部網關協議EGP這個類別&#xff0c;用于自治系統AS之間的路由選擇協議。由于在不同AS內度量路由的“代價”(距離、帶寬、費用等&#xff09;可能不同&#xff0c;因此對于…

elasticsearch 7安裝

問題提前報 max virtual memory areas error max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] 如果您的環境是Linux&#xff0c;注意要做以下操作&#xff0c;否則es可能會啟動失敗 1 用編輯工具打開文件/etc/sysctl.conf 2 …

qml渲染引擎介紹

qml項目啟動入口 Qt Quick項目qml腳本在C++代碼里啟動,main.cpp如下: #include <QGuiApplication> #include <QQmlApplicationEngine>int main(int argc, char *argv[]) {

VUE excel表格導出

js代碼 //下載模板 downloadExl() { // 標題 const tHeader [‘xxx’,xxx,xx名稱,電槍xx,協議xx,snxx]; // key const filterVal [agentName, stationName, equName, channelNumber, manufacturer, sn, ]; // 值 const datas [ { agentName: 你好, stationName: 我們, e…

激光雷達與慣導標定 | Lidar_IMU_Init : 編譯

激光雷達與慣導標定&#xff1a;Lidar_IMU_Init 編譯 功能包安裝安裝ceres-solver-2.0.0 &#xff08;注意安裝2.2.0不行&#xff0c;必須要安裝2.0.0&#xff09; LI-Init是一種魯棒、實時的激光雷達慣性系統初始化方法。該方法可校準激光雷達與IMU之間的時間偏移量和外部參數…