基本避免Memory Leak的技巧 — garylee

 

很多程式初學者比較沒有在注意一些Memory處理上的問題。其實只要平時稍微注意一下,就可以避免掉很多問題,而且也可以讓你的程式更好以及更穩定。

首先,是指標變數內容的問題。通常各位在使用指標時,可能習慣不給予初始值。其實這會造成一些問題。所以建議大家宣告指標變數時一律給予初值,若無法在宣告的同時一併配置記憶體,那麼請給予NULL作為初值。例如:

TList *myList = NULL ;

另 外,許多在delete後,大家可能也就不再理會該指標的內容了。其實,這也是會造成一些麻煩。因為記憶體被delete可能只是系統作一個記號在記憶配 置表中,表示該記憶體是free的。原來的記憶體內還是有原先的內容。但是你的程式一個不注意,又去把原來的指標拿起來用。如果馬上就出錯的話,這樣還 好。偏偏因為原來的內容都還在,所以這個錯誤會延遲產生影響。這種類型的Bug特別難抓!因為,你根本找不到引發問題的所在,如果你有個地方一直發生 Access Violation,可是該地方的程式碼你已經check過十幾次了,還是找不問題,那很可能你就是遇到這樣的問題。以前在寫一些程式時,我就曾經為了這 樣的Bug,弄了三天。可見其傷害有多大。

所以,當delete某指標後,其務必將其內容設為NULL。一方面是使用NULL指標,一定是馬上出錯。所以你的Bug一下就會抓到。另外一個
理由是如果你不小心重複delete同一個物件時,一樣會發生延遲影響的錯誤。但是delete一個NULL指標,在BCB內是什麼也不做。所以不
用擔心會發生問題。

總結以上提供下面幾個比較方便MACRO給大家使用:


SAFENEW
SAFEDELETE
SAFEDELETE_ARRAY

其中,SAFENEW是用來配置記憶體使用。與一般new不同的是,當配置記憶體產生Exception或是配置不出記憶體時,會產生一個訊息視窗警告。並且,將該指標內容設定為 NULL。

而SAFEDELETE再delete前會先檢查該指標是否為NULL。若是就進行delete動作,並且在delete完後將該指標內容設定為NULL。SAFEDELETE_ARRAY也是一樣的作用,只是他是用來刪除陣列指標用的。例如:

TList *myList = NULL ;
char *string = NULL ;

上面這三個Macro你可在本文最後的程式列表找到。

SAFENEW(myList, TList());
SAFENEW(string, char[10]);
...

SAFEDELETE(myList);
SAFEDELETE(string);

如 果你的配置的物件只是暫時性的,也就是說只用在某個function之內。強烈建議你使用auto_ptr。因為,使用了auto_ptr你就無須擔心記 憶體是否被釋放掉。只要離開該function或是某個scope,記憶體肯定會被釋放。你就不用老是想著要在哪裡釋放記憶體。不用在每個return前 面寫上一堆delete動作。如果你的程式是使用goto的方式或者寫的像下面這樣…..拜託改用auto_ptr吧…


TList *myList = new TList() ;
TButton *btn = new TButton(this);
char *ptr = new char ;

...
if(OpenFileFailed)
{
delete myList ;
delete btn ;
delete ptr ;
return ;
}

...
if(OtherFailure)
{
delete myList ;
delete btn ;
delete ptr ;
return ;
}

...
if(SomeThingTrue)
{
delete myList ;
delete btn ;
delete ptr ;
return ;
}

delete myList ;
delete btn ;
delete ptr ;
return ;

如果你的程式配置了記憶體可是你也沒有用goto的方式,也沒有寫成向上面那樣,也沒有使用auto_ptr,也沒有用其它技術來釋放記憶體….

那麼……你該死了……Memory Leak最多的一定就是你…

safepointer.h 列表:

/**
* @file SAFE pointer access include file.
* @author Gary W. Lee
*/

#ifndef __SAFEPOINTER_H__
#define __SAFEPOINTER_H__

#ifdef WINDOWS
#define SP_SHOWMSG(msg) MessageBox(NULL, (msg), ,"ERROR", MB_OK)
#else
#define SP_SHOWMSG(msg) fprintf(stderr, "%s\n", (msg))
#endif

#define SAFENEW(p,t) try { \
if(((p) = new t)==NULL) \
{ \
SP_SHOWMSG("Virtual memory exhausted! Program Exit!"); \
} \
} catch (Exception &e) { \
SP_SHOWMSG(e.Message.c_str()); \
p = NULL ; \
}/// 讓物件記憶體配置後檢查是否配置成功。

#define SAFEDELETE(p) if((p)) \
{ delete (p); p = NULL; } /// 讓物件被delete後,將其值設為NULL。並且在delet物件之前,先確定該指標不為NULL。

#define SAFEDELETE_ARRAY(p) if((p)) \
{ delete [] (p); p = NULL; } /// 讓物件Array被delete後,將其值設為NULL。並且在delet物件之前,先確定該指標不為NULL。

#endif // end of !__SAFEPOINTER_H__

huenlil 發表在 痞客邦 PIXNET 留言(0) 人氣()