先來介紹下流程。
new/new[]
,他的流程是先開闢了一個空間,然後調用了構造函數。delete/delete[]
先調用析構函數,然後釋放內存。
那麼如果new/delete
和new[]/deletep[]
沒有配對會出現什麼問題。
對於這個問題我做了簡單的實驗。
非基礎類型
也就是自己寫的類。
先說下結論
delete
釋放new[]
只會調用第一個的析構函數,導致內存泄漏。delete[]
釋放new
會出bug
。
測試如下
class A{
int a;
public:
A():a(0xffff){
cout<<"A()\n";
}
~A(){
cout<<"~A()\n";
}
virtual void func(){
cout<<"func\n";
}
};
int main() {
A *b=new A[4];
delete [] b;
b =new A();
delete b;
b=new A[4];
delete b;
A *a=new A();
delete[] a;
return 0;
}
輸出結果是
A()
A()
A()
A()
~A()
~A()
~A()
~A()//4個析構
A()
~A()//一個析構
A()
A()
A()
A()
~A()//一個析構
A()
Process finished with exit code -1073741819 (0xC0000005)//直接RE了
問題已經看出來,那麼是爲什呢。
我們去看看內存。
看到了嗎,十分明顯的一個數字,表示分配了多少個,後面很明顯吧。一個虛函數指針,和一個值。
然後執行下一行釋放內存。
看出內存的變化了嗎,全部都釋放掉了,前面那個4 也沒了。
執行到這,用new
創建的明顯沒有一個表示多少個的。
執行下一行
然後沒了被釋放了。
執行後面錯誤的。
看看用delele
刪除new[]
。
執行完後,他們全部都在,我都懷疑內存到底有沒有釋放。很顯然全部都還沒有釋放掉。
然後後面那個爲啥會RE不用我說了吧。
前面那個表述個數的數字這麼大。。你想怎麼搞。。。
小問題1不要慌
對於這個實驗,如果把虛函數刪掉,就不會是內存錯誤而是一個非常大的循環。至於爲什麼自己思考。
結論:
用new
參數的非基礎類型,只會開闢一個對象內存空間,new[]
還會保存一個數組大小。delete[]
刪除的時候會根據 刪除地址前面的4個字節表示的數字大小來刪除後面的對象。
爲了保證正確性,我來做個有意思的事,我們來手寫一個new[]
。
class A{
int a;
public:
A():a(0xffff){
cout<<"A()\n";
}
~A(){
cout<<"~A()\n";
}
virtual void func(){
cout<<"func\n";
}
};
int main() {
void *p=::operator new(sizeof(A)*4+sizeof(int));//這個可以用malloc來是一樣的結果。
int *size=(int *)p;
*size=4;//這個設置一下大小
A *a=(A *)(size+1);//這個就是new[] 的實現
new(a)A();
new(a+1)A();
new(a+2)A();
new(a+3)A();
delete[] a;//顯然這個是釋放
return 0;
}
理論上delete []
也能手寫實現,具體怎麼實現讀者自己動手操作,切忌拿來主義。
基本類型
對於基本類型和非基本類型有什麼區別呢???,emmmm好像沒啥區別。
但是他確實不一樣。
int main() {
int *b=new int[4]{0xfff,0xffff,0xfff,0xfff};
delete [] b;
b =new int();
delete b;
b=new int[4]{0xfff,0xffff,0xfff,0xfff};
delete b;
int *a=new int();
delete[] a;
return 0;
}
運行這個你會發現,沒有任何問題!!!!,確實就是,沒有任何問題。
爲什麼會這樣???。
我們照着前面那個的來。
看到了嗎,沒有那個表示數組長度的東西了。
delete
之後內存變了。
後面的自己看,你會發現,用delet
刪除new[]
完全沒問題。。。
delet[]
刪除new
,好像也沒啥問題。
那麼問題來了,他們是怎麼知道要釋放多少內存的。
這個就比較複雜了。他和free
是一個性質,內存分配是交給操作系統完成的,具體怎麼操作的還是和內核有關。
應該也能很清楚的看見,每次釋放內存,改變的不僅僅是你需要的的內存大小,而是一整塊內存。
所以用delete/delete[]
釋放內存的時候,實際上是直接把那一塊內存釋放了。
結論:
對於基礎類型,用delete/delete[]
刪除實際上是一樣的,主要原因應該還是因爲,他沒有調用構造和析構的必要。如果一個類裏面封裝了一個指針,然後new
了一個對象,如果不調用析構函數,就會出現內存泄漏。