C++中變數的記憶體分布和C語言是一樣的,
這篇文章主要是C++專屬開辟和釋放空間的方式new和delete
文章目錄
- 一、記憶體分布
- 二、C++的動態記憶體管理方式
- 2.1.new和delete開辟內置型別和非自定義變數
- 三、new和delete底層
- 3.1.多載專屬的operator new和operator delete
- 四、定位new運算式(placement-new)
- 五、malloc/free和new/delete的區別
- 六、記憶體泄漏
一、記憶體分布
程式的本質就是為了管理資料和對資料進行處理,為了更好的保存和管理資料,作業系統對記憶體進行了劃分,不同的資料存放的記憶體區域不同:

堆疊區向下增長、堆區向上增長,這也就說明堆疊區開辟的空間是先開辟在高地址,后開辟的空間在低地址,堆區開辟空間先開辟在低地址,后開辟的空間在高地址,當然也會有先開辟的空間被釋放,后開辟的空間占用先開辟的空間,就不符合這種情況了,
注意;const變數雖然有常屬性,但是不是定義在常量區的,一般根據const變數的型別放在堆疊(區域變數)或者靜態區(static修飾),
二、C++的動態記憶體管理方式
C語言中有malloc、realloc、calloc和free的動態記憶體管理方式,由于C++兼容C語言,因此C語言的這些方式可以在C++中繼續使用,但為了方便,C++有自己的動態記憶體管理方式,
即通過new和delete運算子進行動態記憶體管理,
2.1.new和delete開辟內置型別和非自定義變數
其語法為:
void Test()
{
// 動態申請一個int型別的空間
int* ptr1 = new int;
// 動態申請一個int型別的空間并初始化為1
int* ptr2 = new int(1);
// 動態申請10個int型別的空間
int* ptr3 = new int[3];
// 動態申請3個int型別的空間并初始化
int* ptr4 = new int[3]{1, 2, 3};//如果
//釋放掉開辟的空間 delete加變數名即可,如果是陣列要在變數名之前加[]
delete ptr1;
delete ptr2;
delete[] ptr3;
delete[] ptr4;
}
對于內置型別,new 和free與malloc和free沒有區別,
#include<iostream>
using namespace std;
class Test
{
public:
Test()
{
std::cout << "呼叫建構式" << std::endl;
}
~Test()
{
std::cout << "呼叫解構式" << std::endl;
}
private:
int _test;
};
void Test()
{
//申請一個Test型別的物件
Test* p1 = new Test;
delete p1;
cout << endl;
//申請10個Test型別的物件
Test* p2 = new Test[10];
delete[] p2;
cout << endl;
}
int main()
{
Test T;
cout << endl;
Test();
return 0;
}

對于自定義型別,在new時會自動呼叫其建構式初始化,delete時會呼叫其解構式,而malloc和free僅僅只是開空間,

注意delete處理陣列型別別的時候,會對每一個陣列物件都呼叫它們的解構式,然后再釋放它們所占用的記憶體空間,所以如果不加[],記憶體會不匹配導致程式崩潰,不過加不加[]對內置型別沒有區別,因為內置型別只需要釋放空間,所以還是建議加上[]

三、new和delete底層
new和delete是用戶進行動態記憶體申請和釋放的運算子,operator new 和operator delete是系統提供的全域函式(不是函式多載),new在底層呼叫operator new全域函式來申請空間,并且呼叫類的建構式,delete在底層通過operator delete全域函式來釋放空間,并呼叫類的解構式,


所以,operator new與malloc開辟空間失敗后的處理方式不桶,malloc失敗回傳NULL,operator new失敗以后拋例外,


拋例外會捕獲出現錯誤的陳述句從而直接跳轉到catch陳述句中,從而不會執行錯誤陳述句后面的陳述句,如果沒有出錯則會繼續執行,catch不會執行,

綜上,new和delete在底層的原理為:
-
對于內置型別開辟空間,new和malloc,delete和free基本類似,不同的地方是:new/delete申請和釋放的是單個元素的空間,new[]和delete[]申請的是連續空間,而且new在申請空間失敗時會拋例外,malloc會回傳NULL,
-
對于自定義型別,new單個物件時會呼叫operator new函式申請空間并在申請的空間上執行建構式,完成物件的構造,
-
delete單個物件時在空間上執行解構式,完成物件中資源的清理作業并 呼叫operator delete函式釋放物件的空間,
-
對于一個內置型別的陣列,new T[N]呼叫operator new[]函式,在operator new[]中實際呼叫operator new函式完成N個物件空間的申請,并在申請的空間上執行N次建構式,
-
delete[],在釋放的物件空間上執行N次解構式,完成N個物件中資源的清理,并且呼叫operator delete[]釋放空間,實際在operator delete[]中呼叫operator delete來釋放空間
3.1.多載專屬的operator new和operator delete
通過寫了類專屬多載就不用呼叫全域的operator new與operator delete了,

使用這種方式,實作使用記憶體池申請和釋放記憶體,提高效率,
四、定位new運算式(placement-new)
對于一個已經開辟好的記憶體空間,可以顯示地呼叫類的建構式,從而給這塊記憶體初始化一個物件,
定位new運算式在實際中一般是配合記憶體池使用,因為記憶體池分配出的記憶體沒有初始化,所以如果是自定義型別的物件,需要使用new的定義運算式進行顯示調建構式進行初始化,

這種寫法等價于Test* p = new Test;,
五、malloc/free和new/delete的區別
malloc/free和new/delete的共同點是:都是從堆上申請空間,并且需要用戶手動釋放,
不同的地方是:
- malloc和free是函式,new和delete是運算子
- malloc申請的空間不會初始化,new可以初始化
- malloc申請空間時,需要手動計算空間大小并傳遞,new只需在其后跟上空間的型別即可,如果是多個物件,[ ]中指定物件個數即可,
- malloc的回傳值為void*, 在使用時必須強轉,new不需要,因為new后跟的是空間的型別
- malloc申請空間失敗時,回傳的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲例外
- 申請自定義型別物件時,malloc/free只會開辟空間,不會呼叫建構式與解構式,而new在申請空間后會呼叫建構式完成物件的初始化,delete在釋放空間前會呼叫解構式完成空間中資源的清理
六、記憶體泄漏
記憶體泄漏通常是指由于程式員自己的疏忽和程式設計的錯誤等原因導致未能釋放已經不再使用的記憶體的情況,
記憶體泄漏是指向這塊空間的指標丟失了,導致我們無法控制這塊記憶體,但是這塊并沒有還給編譯器,依然被占用,
所以malloc、new申請空間的本質是將一塊記憶體的使用權交給使用者,free、delete釋放記憶體空間的本質是交還使用權給系統,系統就可以再將這塊空間分配給別人,
一般的程式記憶體泄漏危害并不是很大,因為程式行程結束以后就會被釋放掉,
但是對于長期運行的程式如服務器后臺程式危害就非常大,出現記憶體泄漏會導致回應越來越慢,最終卡死,
記憶體泄漏的幾種情況:
- 堆記憶體泄漏(Heap leak)
通過malloc / calloc / realloc / new等從堆中分配的一塊記憶體,用完后必須通過呼叫相應的 free或者delete 刪掉,假設程式的設計錯誤導致這部分記憶體沒有被釋放,那么以后這部分空間將無法再被使用,就會產生Heap Leak, - 系統資源泄漏
指程式使用系統分配的資源,比方套接字、檔案描述符、管道等沒有使用對應的函式釋放掉,導致系統資源的浪費,嚴重可導致系統效能減少,系統執行不穩定,
避免記憶體泄漏的方式:
- 良好的設計規范,養成良好的編碼規范,申請的記憶體空間記著匹配的去釋放,如果碰上例外時,就算注意釋放了,還是可能會出問題,所以需要智能指標來管理,
- 采用RAII思想或者智能指標來管理資源,
- 公司內部規范使用內部實作的私有記憶體管理庫,這套庫自帶記憶體泄漏檢測的功能選項,
- 出問題了使用記憶體泄漏工具檢測,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/301962.html
標籤:其他
