1.幾乎所有程序員都知道的,使用new申請的內存,已不再使用卻沒被delete;
破解之道:
方法很多,
最基礎方法就是程序員自己注意 new 和 delete 配套出現;
比較高級一點的,就是模仿智能指針,使用引用計數器;
2.new出來的內存,被強轉成其他類型,之後被釋放,但內存並沒有被清理乾淨;
比如:(示例僞代碼,不追求可運行)
class Student{
private:
int a;
int b;
}
class Person{
}
void main()
{
Student* pStu = new Student;
Person* pPer = (Person*)pStu;
delete pPer;
}
3.申請時使用 new[],但釋放時使用 delete;
破解之道:
(new -- delete)
(new[] -- delete [])
4.比較不好排查的一種情況
示例,僞代碼,不追求可運行性
class Base{
int a;
}
class Person:public Base{
int b;
}
void main()
{
Base* p = new Person;
delete p;
}
簡單分析:
Person繼承Base基類,所以Person內存中至少存在8字節(int a + int b),
而Base* p = new Person;是合法的,
delete p;也是合法的,
問題在於 delete p 時,只釋放了Base中的資源,也就是(int a)資源,
導致派生類中(int b)資源意外丟失。
解決方法:
使用 虛析構函數
class Base{
public:
virtual ~Base();
int a;
}
class Person:public Base{
public:
virtual ~Person();
int b;
}
5.比較少見的情況
示例:僞代碼,不追求可運行性
class Test{
void* p = new char[32];
}
void main()
{
Test a,b;
memcpy(&a,&b,sizeof(a));
}
簡單分析:
memcpy(&a,&b,sizeof(a));
首先要知道 Test.p 也是4字節的數據塊,只不過這裏存放的是另一塊內存的起始地址,
由於完全拷貝,導致指針 a.p = b.p ,
也就是說原先 a.p 中存放的地址被 b.p 地址覆蓋,
使得原先 a.p 所指向的內存無法再被訪問到;
破解之道:
當然是禁止對類實例進行memcpy,memset這類操作啦!
6.更稀少的情況:析構函數中拋出異常導致內存泄漏
class Test{
...
~Test()
{
...todo //假設因爲某些條件,這裏拋出異常了
...todo
}
}
調用:
std::vector<Test> vTest;
簡單分析:
vTest容器對象被銷燬之前,會先銷燬內部的Test對象實例,但Test析構函數在執行過程中,
因爲某些原因,拋出異常,導致內存未被完全釋放,甚至會引發程序後續一系列不明確行爲;
破解之道:
不要在析構函數中拋出異常,
如果出現異常,可考慮使用std::abort強制中斷程序,
更仁道的做法,是捕捉異常,但可能會有不良的運行後果,
具體如何抉擇,視情況而定,
但是,但是,但是,最好的方法,當然是禁止在析構函數中拋出異常;