內存管理(二)new[] 使用 delete 造成內存泄漏

正文

在說明使用new[]申請內存,而使用delete釋放的問題之前,先了解new[] 和 delete[] 的工作方式。

一.new[]

1.調用operator new[] ,由operator new[]調用n次operator new 來分配n個對象的空間。
2.調用n次構造函數。

二.delete[]

1.調用n次析構函數。
2.調用operator delete[] ,由operator delete[]調operator delete來釋放空間。
先探究一下是否真的是這樣的:

#include<stdio.h>
#include<stdlib.h>
class A
{
public:
 int a;
 ~A()
 {
  printf("A析構\n");
 }
};
void operator delete(void* p)
{
 printf("釋放內存\n");
 free(p);
}int main()
{
 A* p = new A[10];
 delete[] p;
}

運行結果:
A析構
A析構
A析構
A析構
A析構
A析構
A析構
A析構
A析構
A析構
釋放內存

我們重載了operator delete 後,可以看見,先執行了10次析構,然後釋放了一次空間。
這樣 new[] 搭配 delete[] 使用的方式是完全正確且沒有問題的。

三.簡單數據類型的new[] delete

void operator delete(void* p)
{
 printf("釋放內存\n");
 free(p);
}int 
int main()
{
 int *p =  new inr[10];
 delete p;
}

輸出結果: 釋放內存
可以看到對於簡單數據類型,使用new[] 和 delete 並沒有問題(實則也沒有造成內存泄漏)。

四.複雜數據類型的new[] delete

對於類內部沒有自己定義析構函數的類型

#include<stdio.h>
#include<stdlib.h>
class A
{
public:
 int a;
};
void operator delete(void* p)
{
 printf("釋放內存\n");
 free(p);
}int main()
{
 A* p = new A[10];
 delete p;
}

運行結果: 釋放內存

程序正常運行了,且空間正常釋放。所以對於沒有自己定義析構函數的數據類型也是可以的。

對於類內部自己定義析構函數的類型

#include<stdio.h>
#include<stdlib.h>
class A
{
public:
 int a;
 ~A()
 {}
};
void operator delete(void* p)
{
 printf("釋放內存\n");
 free(p);
}int main()
{
 A* p = new A[10];
 delete p;
}

這樣的代碼是不可以運行的,會拋出異常。那麼爲什麼會這樣?
上文說到的,delete[]做的兩個工作,調用n次析構,調用operator delete 釋放空間。編譯器在調用析構的工作上,做出了優化,有些數據類型的析構函數是系統合成的沒有作用的析構函數(trivial destructor),對於這樣的情況是沒必要調用多次析構的,所以系統決定什麼也不做,這樣就只用釋放空間就可以了。同樣,簡單數據類型也是這樣的。
對於空間是否被完全釋放的問題主要取決於類的內部是否由指針需要被釋放,如果沒有,就不存在內存泄漏的問題。因爲我們在分配內存的時候,其實會有兩個cookie來存放這一次所申請內存的空間大小,所以無論是delete[] 還是 delete 都會完整的把申請的內存釋放掉。但是如果,其中含有指針,我們一般會在析構函數中釋放掉指針所指向的內存空間,可是使用delete只會調用一次析構,也就只會釋放一次指針,其餘指針所指向的內存就會泄漏。那麼編譯器要求,對於內部含有nontrivial destructor的類型,在釋放時必須要每一個都得調用到它的析構函數,這樣確保了每個對象內部的指針所指向的內存都會被釋放。

以下方式會造成內存泄漏:

#include<stdio.h>
#include<stdlib.h>
class A
{
public:
 int* a;
 A()
 {
  a = new int;
 }
};
void operator delete(void* p)
{
 printf("釋放內存\n");
 free(p);
}int main()
{
 A* p = new A[10];
 delete p;
}

運行結果:釋放內存

其中泄漏的內存就是每個對象中指針所指向的內存。

總結:
造成內存泄漏的真正原因是因爲使用delete只會執行一次析構函數,倘若類內部有要在析構函數中釋放,就會造成沒有執行析構函數的對象內部指針所指向空間沒有被回收。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章