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?https://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代碼進行交互時的情況。無論使用哪種方法,都需要謹慎管理內存,確保在不再需要時釋放它,以避免內存泄漏。
以上就是本次全部內容,感謝大家觀看!!!?