常見記憶體洩露及解決方案-選自ood啟示錄
new/delete, array new/arrray delete匹配
case 1:
在類的建構函式與析構函式中沒有匹配地呼叫 new/delete!
解決方法:檢查建構函式,在出現new的情況下,按相反的順序在析構函式中匹配新增delete!
這裡有兩個意思:
1〉new與delete匹配,array new/array delete匹配;
2〉出現在前面的new要比出現在後面的new後匹配各自的delete;
比如:建構函式:
m_x = new int[10];
...m_y = new cstring;
則析構函式:
delete m_y;
...delete m_x;// 對於基本資料型別,用delete也可以,但為了統一,還// 是用array delete
case 2:
沒有正確地清除巢狀的物件指標
也就是說,某個物件以引用語義(指標)了包含另乙個物件,而不是以值的方式。
解決辦法:
1〉養成好的成對編碼習慣:
在外部函式分配的堆記憶體,不要在呼叫函式裡面釋放,而在外部函式內釋放;
2〉盡量在建構函式裡面分配記憶體,並注意不要犯case 1錯誤;
3〉在基類/繼承類各管各的記憶體;(具體解析見下面的case 8)
for example:
#include
#include
// melon : 甜瓜,西瓜;
class melon
;melon::melon(char * var)
melon::~melon()
void melon::print()
// meal : 進餐;
class meal
;meal::meal(char * var, char * res)
// 方法2:改引用為值包含;
// : m_melon(var)
meal::~meal()
void meal::print()
int main(...)
case 3:在釋放物件陣列時,沒有使用delete ;
1>對於單個物件,單個基本型別(如int,double等)的變數,我們肯定採用delete,不會出錯;
2>對於基本型別陣列,由於不需要大小引數,因而,採用delete或array delete(delete ),均可以,如上例中,我便直接採用了delete m_variety,建議為了統一,採用delete m_variety;
3>對於自定義的物件所組成的物件陣列,則一定要採用array delete,這樣編譯器才會在釋放記憶體前呼叫每個物件的析構函式,並呼叫
free釋放物件陣列空間;
for example:
#include
#include
class point
;point::point(int x, int y, char *col)
:m_x(x), m_y(y)
point::~point()
int main(int argc, char *argv)
case 4:
指向由指向物件的指標構成的陣列不等同於與物件陣列。
也就是說,陣列的基本型別是指向物件的指標,此時,是用delete 還是delete (array delete),並不重要,關鍵是指標並沒有析構函式,必須使用者自己呼叫delete語句.
for example:
// point類和case 3一樣;
int main(int argc, char *argv)
// 下面語句並沒有釋放10個point物件,釋放的只是他們的指標所組成的陣列
// 占用的10*sizeof(point*) 空間,造成了記憶體洩露
// (180 = 10*sizeof(point) + 10* 6; (6= sizeof("green")))
//delete pptrary;
// 正確的方法:
for (i = 0; i < 10; ++i)
deletepptrary; // 或者delete pptrary;
return 0;
}case 5:
缺少拷貝建構函式
這沒什麼好說的,主要是解決編譯器預設新增的拷貝建構函式不足!預設的拷貝建構函式採用位拷貝,
如下**:
point x;
point y(x);
這樣會導致兩個point物件 x,y的 m_color指向同乙個"red"字串;
當某個物件釋放後,另外乙個物件的 m_color變成懸空指標,從而導致程式異常;
解決方法:
編寫自己的拷貝建構函式;
對於point類,編寫如下:
point::point(const point& y)
: m_x(y.m_x), m_y(y.m_y)
case 6:
缺少過載賦值運算子,理由和上面一樣!
需要注意其實現的細節區別:
1> 拷貝建構函式編譯器會自動阻止自己構造自己,比如:
point x(x); // 出錯;
但是,賦值操作不會;
point x = x; // 編譯期不會出錯,但執行期會出錯!
上面的錯誤原因在於,編譯器雖然為x分配了記憶體,但呼叫拷貝建構函式時,m_color還沒初始化;
建議,盡量不要用這種方法初始化,以便將錯誤在編譯期間顯示出來;
2> 賦值運算子必須區別是否自身賦值;
3> 在賦值前必須釋放原有new操作分配的資源(當然,其他檔案等資源也要釋放,這裡只討論記憶體溢位,略過不提!)
最後實現如下:
const point& point::operator =(const point& rhs)
return *this;
}注意,最左邊的const宣告可以不要,要得話是為了阻止如下語句:
(x = y) = z;
但由於基本型別也支援,為了與基本型別一致,可以去掉const約束;
case 7:
關於nonmodifying運算子過載的常見錯誤;
所謂nonmodifying運算子就是不改變運算元的值,並且返回結果型別與運算元一樣;比如數**算符;
而關係運算子則不滿足,因為其結果為bool型;
賦值運算子也不是(=, += ,<<=等等);
主要原因是,大家可能將結果儲存到乙個區域性變數裡面,而返回結果為了效率採用了引用(&);
解決方法:
1> 利用static, 將臨時變數作為類的內部儲存單元;
不足,不適合巢狀使用和多執行緒,比如 w = x+y+z;
for example:
// case 7,解決方法1:static
const point& point::operator +(const point& rhs) const
注意,這裡為了簡單,並沒有考慮型別轉換,實際中二元運算子通常採用友元函式形式實現,具體判斷方法請看effective c++ item 19;
2> 改引用語義為值語義;(最好辦法,但會降低效率)
注意,有人也許會用指標方法,比如如下:
point *temp = new point;
...return (*temp);
這樣會產生乙個無名物件,並且位於堆上,從而造成記憶體洩露;
const point point::operator +(const point& rhs) const
case 8:
沒用將基類的析構函式定義成虛函式;
解決方法:
將基類的析構函式定義為虛函式;
注意:1> 該錯誤具有隱蔽性,當所有派生類均沒有新的new操作時,不會產生記憶體溢位;因而,最好遵循以下原則:
將基類建構函式定義為非虛函式,則該類不允許擴充套件;
2> 如果不是虛函式,則釋放基類指標不會呼叫派生類的析構函式,即使它指向乙個派生類物件;
3> 不管是不是虛函式,釋放派生類指標均會呼叫基類的析構函式,且呼叫順序不變;
4> 如果為虛函式,則釋放基類指標且該指標指向乙個派生類,則會先呼叫派生類的析構函式,再呼叫基內的析構函式!
常見記憶體洩露及解決方案
常見記憶體洩露及解決方案 常見記憶體洩露及解決方案 選自ood啟示錄 new delete,array new arrray delete匹配 case 1 在類的建構函式與析構函式中沒有匹配地呼叫 new delete!解決方法 檢查建構函式,在出現new的情況下,按相反的順序在析構函式中匹配新增...
C 常見記憶體錯誤及解決方案
c 中記憶體錯誤通常屬於執行時錯誤,只有在程式執行時才能發現,編譯器無法自動檢測到記憶體錯誤。多數情況下是程式邏輯或者引數存在某些錯誤。下面總結一下c 常見的記憶體錯誤 1.記憶體洩露 記憶體洩露是指應用程式未釋放動態申請的且不再使用的記憶體,原因可能是程式設計師疏忽或者錯誤造成程式異常。在c c ...
C 常見記憶體錯誤及解決方案
c 常見記憶體錯誤及解決方案 c 中記憶體錯誤通常屬於執行時錯誤,只有在程式執行時才能發現,編譯器無法自動檢測到記憶體錯誤。多數情況下是程式邏輯或者引數存在某些錯誤。下面總結一下c 常見的記憶體錯誤 1.記憶體洩露 記憶體洩露是指應用程式未釋放動態申請的且不再使用的記憶體,原因可能是程式設計師疏忽或...