c++中的內存泄漏的原因


1. 在類的構造函數和析構函數中沒有匹配的調用new和delete函數

兩種情況下會出現這種內存泄露:一是在堆里創建了對象占用了內存,但是沒有顯示地釋放對象占用的內存;二是在類的構造函數中動態的分配了內存,但是在析構函數中沒有釋放內存或者沒有正確的釋放內存

2. 沒有正確地清除嵌套的對象指針

3. 在釋放對象數組時在delete中沒有使用方括號

方括號是告訴編譯器這個指針指向的是一個對象數組,同時也告訴編譯器正確的對象地址值並調用對象的析構函數,如果沒有方括號,那么這個指針就被默認為只指向一個對象,對象數組中的其他對象的析構函數就不會被調用,結果造成了內存泄露。如果在方括號中間放了一個比對象數組大小還大的數字,那么編譯器就會調用無效對象(內存溢出)的析構函數,會造成堆的奔潰。如果方括號中間的數字值比對象數組的大小小的話,編譯器就不能調用足夠多個析構函數,結果會造成內存泄露。

釋放單個對象、單個基本數據類型的變量或者是基本數據類型的數組不需要大小參數,釋放定義了析構函數的對象數組才需要大小參數。

4. 指向對象的指針數組不等同於對象數組

對象數組是指:數組中存放的是對象,只需要delete []p,即可調用對象數組中的每個對象的析構函數釋放空間

指向對象的指針數組是指:數組中存放的是指向對象的指針,不僅要釋放每個對象的空間,還要釋放每個指針的空間,delete []p只是釋放了每個指針,但是並沒有釋放對象的空間,正確的做法,是通過一個循環,將每個對象釋放了,然后再把指針釋放了。

5. 缺少拷貝構造函數

兩次釋放相同的內存是一種錯誤的做法,同時可能會造成堆的奔潰。

按值傳遞會調用(拷貝)構造函數,引用傳遞不會調用。

在C++中,如果沒有定義拷貝構造函數,那么編譯器就會調用默認的拷貝構造函數,會逐個成員拷貝的方式來復制數據成員,如果是以逐個成員拷貝的方式來復制指針被定義為將一個變量的地址賦給另一個變量。這種隱式的指針復制結果就是兩個對象擁有指向同一個動態分配的內存空間的指針。當釋放第一個對象的時候,它的析構函數就會釋放與該對象有關的動態分配的內存空間。而釋放第二個對象的時候,它的析構函數會釋放相同的內存,這樣是錯誤的。

所以,如果一個類里面有指針成員變量,要么必須顯示的寫拷貝構造函數和重載賦值運算符,要么禁用拷貝構造函數和重載賦值運算符

 C++中構造函數,拷貝構造函數和賦值函數的區別和實現參見:http://www.cnblogs.com/liushui-sky/p/7728902.html

6. 缺少重載賦值運算符

這種問題跟上述問題類似,也是逐個成員拷貝的方式復制對象,如果這個類的大小是可變的,那么結果就是造成內存泄露,如下圖:

7. 關於nonmodifying運算符重載的常見迷思

a. 返回棧上對象的引用或者指針(也即返回局部對象的引用或者指針)。導致最后返回的是一個空引用或者空指針,因此變成野指針

b. 返回內部靜態對象的引用。

c. 返回一個泄露內存的動態分配的對象。導致內存泄露,並且無法回收

解決這一類問題的辦法是重載運算符函數的返回值不是類型的引用,二應該是類型的返回值,即不是 int&而是int

8. 沒有將基類的析構函數定義為虛函數

當基類指針指向子類對象時,如果基類的析構函數不是virtual,那么子類的析構函數將不會被調用,子類的資源沒有正確是釋放,因此造成內存泄露

9.析構的時候void*,導致析構函數沒有調用

10.構造的時候淺拷貝,釋放的時候調用了兩側delete

11.野指針:指向被釋放的或者訪問受限內存的指針。

造成野指針的原因:

指針變量沒有被初始化(如果值不定,可以初始化為NULL)
指針被free或者delete后,沒有置為NULL, free和delete只是把指針所指向的內存給釋放掉,並沒有把指針本身干掉,此時指針指向的是“垃圾”內存。釋放后的指針應該被置為NULL.
指針操作超越了變量的作用范圍,比如返回指向棧內存的指針就是野指針。

解決辦法:

(1)shared_ptr共享的智能指針:

shared_ptr使用引用計數,每一個shared_ptr的拷貝都指向相同的內存。在最后一個shared_ptr析構的時候,內存才會被釋放。

注意事項: 
1.不要用一個原始指針初始化多個shared_ptr。 
2.不要再函數實參中創建shared_ptr,在調用函數之前先定義以及初始化它。 
3.不要將this指針作為shared_ptr返回出來。 
4.要避免循環引用。

(2)unique_ptr獨占的智能指針:

<1>Unique_ptr是一個獨占的智能指針,他不允許其他的智能指針共享其內部的指針,不允許通過賦值將一個unique_ptr賦值給另外一個 unique_ptr。

<2>unique_ptr不允許復制,但可以通過函數返回給其他的unique_ptr,還可以通過std::move來轉移到其他的unique_ptr,這樣它本身就不再 擁有原來指針的所有權了。

<3>如果希望只有一個智能指針管理資源或管理數組就用unique_ptr,如果希望多個智能指針管理同一個資源就用shared_ptr。

(3)weak_ptr弱引用的智能指針:
弱引用的智能指針weak_ptr是用來監視shared_ptr的,不會使引用計數加一,它不管理shared_ptr內部的指針,主要是為了監視shared_ptr的生命 周期,更像是shared_ptr的一個助手。 weak_ptr沒有重載運算符*和->,因為它不共享指針,不能操作資源,主要是為了通過shared_ptr獲得資源的監測權,它的構造不會增加引用計數,它的析構不會減少引用計數,純粹只是作為一個旁觀者來監視shared_ptr中關連的資源是否存在。 weak_ptr還可以用來返回this指針和解決循環引用的問題。
(4)

set_new_handler(out_of_memroy); //注意參數傳遞的是函數的地址;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM