在C語言中,咱們寫程序時,老是會有動態開闢內存的需求,每到這個時候咱們就會想到用malloc/free 去從堆裏面動態申請出來一段內存給咱們用。但對這一塊申請出來的內存,每每還須要咱們對它進行稍許的「加工」後即初始化 才能爲咱們所用,雖然C語言爲咱們提供了calloc來開闢一段初始化好(0)的一段內存,但面對象中各是各樣的數據成員初始化,它一樣一籌莫展。同時,爲了保持良好的編程習慣,咱們也都應該對申請出來的內存做手動進行初始化。c++
對此,這經常讓咱們感到一絲繁瑣,因而到了C++中就有了new/delete, new []/delete[] 。用它們即可實現動態的內存管理。編程
new/delete動態管理對象,new[]/delete[]動態管理對象數組。數組
在C++中,把int 、char..等內置類型的變量也看做對象,它們也是存在構造函數和析構函數的,只是一般對它們,系統調用了默認的構造函數來初始化以及默認的析構(編譯器優化)。因此new int、new int(3)看起來和普通的定義好像沒什麼區別。 但對於自定義類型的對象,此種方式在建立對象的同時,還會將對象初始化好;因而new/delete、new []/delete []方式管理內存相對於malloc/free的方式管理的優點就體現出來了,由於它們能保證對象一被建立出來便被初始化,出了做用域便被自動清理。ide
* malloc/free只是動態分配內存空間/釋放空間。而new/delete除了分配空間還會調用構造函數和析構函數進行初始化與清理(清理成員)。函數
* 它們都是動態管理內存的入口。
* malloc/free是C/C++標準庫的函數,new/delete是C++操做符。
* malloc/free須要手動計算類型大小且返回值w爲void*,new/delete可自動計算類型的大小,返回對應類型的指針。優化
* malloc/free管理內存失敗會返回0,new/delete等的方式管理內存失敗會拋出異常。spa
儘管看起來new、new[] 和malloc 都能開得空間出來,而且以new 、new[]的方式好像還更有優點。但從系統層面看來,真正開出空間來的仍是malloc。爲何這麼說呢?指針
在C++ Primer書中有提到說: new/delete的表達式與標準庫函數同名了,因此係統並無重載new或delete表達式。new/delete真正的實現實際上是依賴下面這幾個內存管理接口的。c++中稱之爲「placement版」內存管理接口調試
接口原型:code
void * operator new (size_t size); void operator delete (size_t size); void * operator new [](size_t size); void operator delete[] (size_t size);
探究它,不妨從這樣一個類AA開始
1 class AA 2 { 3 public: 4 AA(size_t count = 1) 5 { 6 _a = new int[count]; 7 cout<<"AA()"<<endl; 8 } 9 10 ~AA() 11 { 12 delete[] _a; 13 cout<<"~AA()"<<endl; 14 } 15 16 private: 17 int* _a; 18 };
用AA* pA = new AA[10]建立對象,VS下經過調試進入new表達式內部系統函數,獲得下面兩個圖:
和
經過上面兩個圖,大體能夠看出來new表達式並不直接開闢內存出來,而是經過調用operator new來得到的內存,而operator new得到的內存實質上仍是用malloc開闢出來的。這便證明了前面所述的:開空間出來仍是得 malloc來。
一樣的道理,delete表達式也不是直接去釋放掉內存。好比對上面的對象數組進行delete
AA* pA = new AA[10]; delete[] pa;
delete[]實際作了這樣幾件事情:
* 依次調用pA指向對象數組中每一個對象的析構函數,共10次
* 調用operator delete[](),它將再調用operator delete
* 底層用free執行operator delete表達式,依次釋放內存
綜合相關資料,小結一下operator new/ operator delete:
1.operator new/operator delete operator new[]/operator delete[] 和 malloc/free用法同樣。
2. 他們只負責分配空間/釋放空間,不會調用對象構造函數/析構函數來初始化/清理對象。
3. 實際operator new和operator delete只是malloc和free的一層封裝
若是仔細看過上面的圖,可能會有疑惑:new最後將開闢好內存用指針p返回,pA接收它。可爲何p 和pA 會差上4字節?
這實際上是由於編譯器用相差的這4個字節用來保存一個東西——對象個數,即AA* p = new AA[10] 中的‘10’。這也就不難解釋 爲何在delete[] 的時候,不用傳給它對象個數。
delete[] 刪除時,將new[] 返回的地址再往前移4個字節即可以拿到要析構的對象個數了。
可是注意:new type[] ,只有type顯示定義析構函數時,編譯器纔會多開4字節來保存對象個數。因此像new int、char這樣的內置類型編譯器不會多開這4字節,編譯器自行優化。
它們之間可用下面的圖展現:
咱們new 出來多少個對象,就得調用多少次析構來對它們進行清理。在用new/delete,new[]/delete[], malloc/free進行內存的管理時,必定不能將它們搞混淆,使用它們必定記得配套使用。
來看幾個例子,仍是之前面AA類爲例
1 class AA 2 { 3 public: 4 AA(size_t count = 1) 5 { 6 _a = new int[count]; 7 cout<<"AA()"<<endl; 8 } 9 10 ~AA() 11 { 12 delete[] _a; 13 cout<<"~AA()"<<endl; 14 } 15 16 private: 17 int* _a; 18 };
1.malloc/delete的組合
void Test1() { AA* p1 = (AA*)malloc(sizeof(AA)); //沒有報錯,但不建議採用,容易引發混淆 delete p1; AA* p2 = (AA*)malloc(sizeof(AA)); //報錯,同上,釋放位置也不對 delete[] p2; }
2.delete, delete[] 之間誤用(值得注意)
void Test2() { AA* p3 = new AA; //不報錯,但未清理乾淨。p3的構造函數開闢的空間沒有被釋放 free(p3); AA* p4 = new AA[10]; //崩潰卡死,存在問題,釋放位置被後移了4字節。同時只調用了一次析構函數
delete p4; ,
AA* p5 = new AA; //報錯 非法訪問內存
delete[] p5;
}
①delete p4錯誤在於釋放位置不對(和編譯器實現new []的機制有關),致使內存泄漏
②delete[] p5 直接就崩了,此次new AA的時候並未多開4字節保存對象個數,編譯器便沒法知道要調用多少次析構函數(這裏僅僅調用一次析構函數就行了)但編譯器內部仍是試圖去訪問p5前4字節的內存,以此得到對象個數;這便非法內存訪問了,因此程序就掛了。
3.針對內置類型
void Test3() { int* p6 = new int[10]; //沒問題 delete[] p6; int* p7 = new int[10]; //沒問題 delete p7; int* p8 = new int[10]; //沒問題 free(p8); }
內存管理內置類型,它們的析構函數其實上是可調可不調的,因此它的實現機制不像前面的new []/delete[],編譯器會自行對處理的數據作記錄,而後處理;因此即使是不匹配的使用,它們也沒出現什麼問題。不只僅這種內置類型如此,那種無自定義類型析構函數的類對象,這樣的用法一樣不會表現出什麼問題。但即使如此,爲保存良好的編程習慣,仍是要配對地使用它們!
結合前面new/delete 的實現機制,便不難分析得出它們若未配對使用可能出現的狀況。
總的來講,記住一點便可:new/delete、new[]/delete[] 配套使用老是沒錯的!