版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
C語言軟件編程規(guī)范工作組公司常見軟件編程低級(jí)錯(cuò)誤:內(nèi)存泄漏
前言這套材料作為編程規(guī)范的輔助材料,幫助大家理解編程規(guī)范背后的原理。C和C++語言是我司的主流編程語言,然而C/C++具有很多強(qiáng)大的語言特性,從而導(dǎo)致C/C++非常復(fù)雜,使得代碼更容易出現(xiàn)BUG、難以閱讀和維護(hù)。業(yè)界知名的編程規(guī)范都對(duì)C/C++容易出現(xiàn)問題的語言特性進(jìn)行管理。例如MISRA(汽車工業(yè)軟件可靠性聯(lián)合會(huì))制定的1998版的MISRAC規(guī)范指出,一些在C看來可以接受,卻存在隱患的地方有127處之多。2004版的MISRAC規(guī)范將針對(duì)C語言的規(guī)則增加到了141條。對(duì)于程序員來說,能工作的代碼并不等于“好”代碼?!昂谩贝a的指標(biāo)很多,包括可讀性、可維護(hù)性、可移植性和可靠性等。出現(xiàn)網(wǎng)上問題的代碼,大多數(shù)是不良編程習(xí)慣引起的。不遵守編程規(guī)范的代碼,往往也是最不可靠的代碼。本膠片收集了常見的內(nèi)存泄漏案例,給出了相應(yīng)的糾正措施。對(duì)應(yīng)的編程規(guī)范:防止內(nèi)存泄漏;函數(shù)中分配的內(nèi)存,在函數(shù)退出之前要釋放內(nèi)存泄漏案例問題和糾正措施建議異常出口處沒有釋放內(nèi)存【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:pMsgDB_DEV=(PDBDevMsg)GetBuff(sizeof(DBDevMsg),__LINE__);if(pMsgDB_DEV==NULL){ return;}
pMsgDBApp_To_Logic=(LPDBSelfMsg)GetBuff(sizeof(DBSelfMsg),__LINE__);if(pMsgDBApp_To_Logic==NULL){ return;}【問題定位】在第2個(gè)return處,pMsgDB_DEV指向的內(nèi)存丟失異常出口處沒有釋放內(nèi)存(續(xù)1)【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:/*在AVL樹中添加節(jié)點(diǎn)*/IF_VC_AVL_AddNode(&stAVLNodeKey,pstBasPortIndex,IF_VC_AVL_CompareNode)ulRet=IF_BAS_VC_CreateVC((*pstBasPortIndex),ulIfIndex);if(ulRet!=VOS_OK)/*創(chuàng)建VC控制塊失敗*/{
return;}
【問題定位】創(chuàng)建VC控制塊失敗時(shí),return前沒有刪除AVL樹中的節(jié)點(diǎn)【舉一反三】看見return要注意,要去前面找資源,特別要注意鏈表等數(shù)據(jù)結(jié)構(gòu)中的資源異常出口處沒有釋放信號(hào)量資源【問題描述&定位】代碼飛檢發(fā)現(xiàn)如下代碼:rc=np_ss_semB_create(NP_SEM_EMPTY,NP_SEM_Q_FIFO,(g_ims_vport_base_info.qinq_base_info.sem));if(rc!=NP_RC_SUCCESS)/*申請(qǐng)信號(hào)量失敗*/
{ NP_SS_ASSERT(0,"Createqinqsemfailed!"); returnrc;
}#ifSOFT_Versionrc=np_ss_semB_create(NP_SEM_EMPTY,NP_SEM_Q_FIFO,&(g_ims_vport_base_info.eqinq_base_info.sem));if(rc!=NP_RC_SUCCESS)/*申請(qǐng)信號(hào)量失敗*/
{NP_SS_ASSERT(0,"Createqinqsemfailed!"); returnrc;//沒有釋放前面分配的信號(hào)量g_ims_vport_base_info.qinq_base_info.sem}#endif
………….NP_FREE_SEM(g_ims_vport_base_info.qinq_base_info.sem);#ifSOFT_VersionNP_FREE_SEM(g_ims_vport_base_info.eqinq_base_info.sem);#endif returnrc;異常出口處沒有釋放信號(hào)量資源(續(xù))【糾正措施】第二個(gè)信號(hào)量申請(qǐng)失敗return之前釋放第一個(gè)申請(qǐng)的信號(hào)量。。。。。。。。。。。。#ifSOFT_Versionrc=np_ss_semB_create(NP_SEM_EMPTY,NP_SEM_Q_FIFO,&(g_ims_vport_base_info.eqinq_base_info.sem));if(rc!=NP_RC_SUCCESS)/*申請(qǐng)信號(hào)量失敗*/
{NP_SS_ASSERT(0,"Createqinqsemfailed!"); NP_FREE_SEM(g_ims_vport_base_info.qinq_base_info.sem);returnrc;}#endif。。。。。。。。。。。。 【舉一反三】看見return要注意,要去前面找資源,特別要注意信號(hào)量、定時(shí)器等資源異常出口處沒有釋放GUI資源【問題描述】網(wǎng)上問題案例:
CBitmapbmp;
CBitmap*pOldBmp;
bmp.LoadBitmap(IDB_MYBMP);
pOldBmp=pDC->SelectObject(&bmp);
if(Something()){
return;
}
pDC->SelectObject(pOldBmp);【問題定位】return前沒有調(diào)用SelectObject()把pOldBmp選回pDC中,這會(huì)導(dǎo)致pOldBmp指向的HBITMAP對(duì)象發(fā)生泄漏。這個(gè)程序如果長時(shí)間的運(yùn)行,會(huì)導(dǎo)致系統(tǒng)花屏【舉一反三】除了申請(qǐng)的內(nèi)存外,系統(tǒng)提供的其它資源,如文件句柄/Socket/隊(duì)列等也是資源,使用完畢必須釋放沒有釋放結(jié)構(gòu)的成員指針【問題描述】網(wǎng)上問題案例:structSTORE_BUF_S{ULONGulLen;UCHAR*pcData;}STORE_BUF_T;voidfunc(){STORE_BUF_T*pstStorageBuff=NULL;
//申請(qǐng)結(jié)構(gòu)內(nèi)存
//程序處理。。。free(pstStorageBuff);
return;}刪除了pstStorageBuff,但pstStorageBuff->pcData沒有刪除。【問題定位】先刪除了pstStorageBuff,pstStorageBuff->pcData永遠(yuǎn)不可能被刪除了【舉一反三】刪除結(jié)構(gòu)指針時(shí),必須從底層向上層順序刪除沒有釋放數(shù)組的成員指針【問題描述】測試部對(duì)M產(chǎn)品進(jìn)行壓力和穩(wěn)定性測試,模擬文件上傳的場景,把本地目錄下的3萬個(gè)文件上傳到另一臺(tái)主機(jī)。發(fā)現(xiàn)上傳程序在傳送文件過程中,內(nèi)存在快速增長,通過psauwx監(jiān)控,發(fā)現(xiàn)該進(jìn)程占用的內(nèi)存每隔4分鐘(一個(gè)周期)就突然增加20~30M的內(nèi)存。【問題定位】structdirent**namelist;intn=scandir(path.c_str(),&namelist,0,alphasort);【1】
inti=0;for(i;i<n;++i)
{stringname=namelist[i]->d_name;free(namelist[i]);【2】if(name!=".."&&name!=".")
{
.........++fileNum;if(MAX_SCAN_FILE_NUM==fileNum)//MAX_SCAN_FILE_NUM=1000
{break;
}
}
}free(namelist);【3】return;沒有釋放數(shù)組的成員指針(續(xù))從上面的代碼可以看是指針數(shù)組namelist由系統(tǒng)函數(shù)進(jìn)行分配內(nèi)存(如【1】所示),內(nèi)存釋放時(shí)時(shí)分別由【2】【3】完成的。但是中間有個(gè)條件,每次只取1000個(gè)文件,如果目錄下的文件大于1000就跳出,后面的就不會(huì)再管了(【2】沒有執(zhí)行到)。所以導(dǎo)致原來本地目錄下文件數(shù)比較小,小于等1000時(shí)沒有內(nèi)存泄漏;而當(dāng)本地目錄下的文件比較多,大于1000時(shí),就會(huì)導(dǎo)致內(nèi)存泄漏?!九e一反三】開發(fā)人員在使用指針數(shù)組時(shí),要特別注意,確保在釋放數(shù)組時(shí),數(shù)組中的每個(gè)元素指針是否已經(jīng)提前被釋放了,這樣才不會(huì)導(dǎo)致內(nèi)存泄漏。重復(fù)分配內(nèi)存【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:ULONGCQC_CmdNoDropLevelClass(VOID*pMsgRcv,VOID**ppMsgSnd){/*以下是從別處拷貝的代碼*/ulErrCode=CFG_CreateResMsgs(pMsgRcv,ppMsgSnd));………………/*拷貝代碼結(jié)束*/………………ulErrCode=CFG_CreateResMsgs(pMsgRcv,ppMsgSnd));
………………}【舉一反三】代碼Copy要小心沒有釋放傳入定時(shí)器的內(nèi)存【問題描述】使用purify測試,發(fā)現(xiàn)上報(bào)MLK:在我們的應(yīng)用程序的對(duì)象中,由于在設(shè)置定時(shí)器時(shí)需要傳遞一個(gè)參數(shù),這個(gè)參數(shù)的需要從堆中去獲取,因?yàn)樵诤罄m(xù)的定時(shí)器超時(shí)回調(diào)時(shí)需要使用這個(gè)參數(shù)。在對(duì)象中設(shè)置定時(shí)器的代碼如下:this->setTimer(timerId,pending_user_enroll_timer_id,30,1,//設(shè)置一個(gè)30秒的定時(shí)器,定時(shí)1次newTString(ipport));在定時(shí)器的超時(shí)處理程序簡化后如下:voidSession::onTimeOut(TInt4timerID,TInt4userTimerID,void*pData){switch(userTimerID){casepending_user_enroll_timer_id:
{TString*ipport=reinterpret_cast<TString*>(pData);...deleteipport;
}...}}在我們?cè)O(shè)置的定時(shí)器超時(shí)后,會(huì)自動(dòng)調(diào)用onTimeOut這個(gè)函數(shù),根據(jù)userTimerID來把所需要的參數(shù)轉(zhuǎn)化為我們?cè)瓉淼臄?shù)據(jù)類型,使用完成后在銷毀它。這看起來一切都很正常,new出來資源通過delete來進(jìn)行釋放,為什么出現(xiàn)了內(nèi)存泄漏呢?沒有釋放傳入定時(shí)器的內(nèi)存(續(xù))【問題定位】定時(shí)器設(shè)置之后在未超時(shí)的時(shí)候,這個(gè)定時(shí)器的所在的對(duì)象就結(jié)束了,定時(shí)器自然就消失了,也就是我們new出來的東西也就消失了,內(nèi)存泄漏就此產(chǎn)生了。【糾正措施】使用智能指針進(jìn)行解決,資源的釋放操作由C++語言特性進(jìn)行保證(在對(duì)象的生命周期結(jié)束調(diào)用其析構(gòu)函數(shù)),具體方案如下:在該對(duì)象類添加一個(gè)智能指針類型的私有成員變量:private:std::auto_ptr<TString>timer_arg_ipport_;這樣,在該對(duì)象被銷毀時(shí),根據(jù)智能指針的特性,由timer_arg_ipport_這個(gè)變量持有的資源會(huì)自動(dòng)被釋放。這樣,我們就不用擔(dān)心資源的泄漏問題了。相應(yīng)的,設(shè)置定時(shí)器的代碼就變?yōu)轭愃迫缦铝耍簍imer_arg_ipport_=std::auto_ptr<TString>(newTString(ipport));this->setTimer(timerId,pending_user_enroll_timer_id,30,1,//設(shè)置一個(gè)30秒的定時(shí)器,定時(shí)1次timer_arg_ipport_.get());同時(shí),在onTimerOut函數(shù)里也不用進(jìn)行delete的調(diào)用了?!九e一反三】資源的分配與釋放不在同一個(gè)地方,可以考慮使用智能指針類型轉(zhuǎn)換不當(dāng):刪除的對(duì)象類型不正確【問題描述】網(wǎng)上問題案例:void*CPtrList::RemoveHead(){}//返回為void*無類型方式的通用指針typedefstructtagPropInfo{//m_PropList中的節(jié)點(diǎn),其中的成員變量有自己的構(gòu)造和析構(gòu)函數(shù) CStringPropName; CStringPropValue;}PropInfo;while(!m_PropList->IsEmpty()){deletem_PropList->RemoveHead();}【問題定位】調(diào)用deletem_PropList->RemoveHead()時(shí),C++編譯器認(rèn)為delete一個(gè)void*的對(duì)象,并沒有調(diào)用類PropInfo的析構(gòu)函數(shù),導(dǎo)致內(nèi)存泄漏【糾正措施】
將m_PropList->RemoveHead()的返回值強(qiáng)制進(jìn)行一次類型轉(zhuǎn)換【舉一反三】盡量避免將多個(gè)功能寫作一條語句中,如果上面的代碼使用了臨時(shí)變量:void*tempPropInfo=m_PropList->RemoveHead();delete(PropInfo*)tempPropInfo;則很容易發(fā)現(xiàn)tempPropInfo的類型不正確,就不會(huì)忘記進(jìn)行類型轉(zhuǎn)換了對(duì)需要釋放的指針重新賦值【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:structFileInfo*pdbffile=newstructFileInfo;pfile=pdbffile;
【問題定位】pfile不為空,pfile以前指向的內(nèi)存丟失了【舉一反三】盡量不要對(duì)指針重新賦值。對(duì)指針重新賦值,首先考慮設(shè)計(jì)是否合理;除建立鏈表等特殊情況外,不要將非空指針作為左值。宏里面有return語句【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:頭文件中的宏定義如下:#defineACE_NEW_RETURN(POINTER,CONSTRUCTOR,RET_VAL)\do{POINTER=newCONSTRUCTOR;\ if(POINTER==0){error=ENOMEM;returnRET_VAL}\ }while(0)C++文件中的語句如下:ACE_NEW_RETURN(g_pProcTimer,CNMTimer(PROC,ONE_SECOND),IM_NM_NEW_FAIL);ACE_NEW_RETURN(g_pBPTimer,CNMTimer(PROC,FIVE_SECOND),IM_NM_NEW_FAIL)【問題定位】當(dāng)?shù)诙€(gè)ACE_NEW_RETURN語句在宏里面出錯(cuò)直接return時(shí),第一個(gè)ACE_NEW_RETURN語句申請(qǐng)的內(nèi)存泄漏了!宏里面有return語句(續(xù)1)【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:頭文件中的宏定義如下:#defineERROR_HANDLE_RETURN(CONDITION,RETURNVALUE){if(true==(CONDITION)){returnRETURNVALUE;}}C++文件中的語句如下:NEW(pShell,MessageHandlerShell);……IRegisting*pRegisting=pService->getFeatureRegist();ERROR_HANDLE_RETURN(NULL==pRegisting,False);【問題定位】一旦宏ERROR_HANDLE_RETURN執(zhí)行retrurn,前面分配的內(nèi)存pShell就會(huì)泄漏?!九e一反三】小心使用有return語句的宏,確保前面資源已經(jīng)釋放。宏里面有return語句(續(xù)2)【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼://將Session從BusySession池移出,放入Free
Session池ERROR_HANDLE_RETURN(FALSE==this->pop(enum_session_pool_busy,pSession),FALSE);ERROR_HANDLE_RETURN(FALSE==this->onIdleSession(pSession),FALSE);ERROR_HANDLE_RETURN(False==this->push(enum_session_pool_free,pSession),False);【問題定位】一旦從第2個(gè)ERROR_HANDLE_RETURN處退出,pSession已經(jīng)從BusySession池移出,但并沒有放入Free
Session池,也沒有被刪除【舉一反三】宏里面有return語句,很容易造成各種隱患,產(chǎn)品最好禁止這種做法。
C++的析構(gòu)函數(shù)沒有釋放內(nèi)存【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:CDBOperation::~CDBOperation(){}【問題定位】上面代碼忘記對(duì)類成員指針刪除,導(dǎo)致內(nèi)存泄漏【糾正措施】
修改代碼為:CDBOperation::~CDBOperation(){If(NULL!=m_pmmlProcssTimeCost){DELETE(m_pmmlProcssTimeCost);}}【舉一反三】類或函數(shù)申請(qǐng)的資源應(yīng)該在退出時(shí)釋放使用復(fù)雜語句或函數(shù):多個(gè)判斷條件寫在一起【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:char*pszInfoBuf=VOS_NULLPTR;char*pszInfoBuf2=VOS_NULLPTR;pszInfoBuf=(char*)VOS_Malloc(MID_BVLAN,ulBuffLen);pszInfoBuf2=(char*)VOS_Malloc(MID_BVLAN,ulBuffLen);if((pszInfoBuf==NULL)||(pszInfoBuf2==NULL)){
returnVOS_ERR;}【問題定位】當(dāng)pszInfoBuf申請(qǐng)成功,但pszInfoBuf2申請(qǐng)失敗時(shí),進(jìn)入此分支,pszInfoBuf指向的內(nèi)存泄漏了
【舉一反三】 不要將多個(gè)判斷條件寫在一起
。使用復(fù)雜語句或函數(shù):沒有及時(shí)釋放不使用的內(nèi)存【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:ULONGCQC_CmdNoApplySystem(VOID*pMsgRcv,VOID**ppMsgSnd)代碼太長,這里不引用。【問題定位】該函數(shù)共531行,函數(shù)開頭就申請(qǐng)了pMsgRcv指針,從128行開始,即不再使用這個(gè)指針,但沒有及時(shí)釋放,一直到531行函數(shù)完全結(jié)束時(shí)才釋放pMsgRcv指針。但在128行以后,共有9處異常退出,有5處釋放了pMsgRcv指針,有4處卻沒有釋放pMsgRcv指針?!九e一反三】1。盡量避免過于復(fù)雜的函數(shù)2。養(yǎng)成及時(shí)釋放內(nèi)存和資源的習(xí)慣。使用復(fù)雜語句或函數(shù):一個(gè)語句,多個(gè)分配【問題描述】網(wǎng)上問題案例Fun(shared_ptr<Widget>(newWidget),shared_ptr<Widget>(newWidget))【問題定位】函數(shù)Fun的兩個(gè)入?yún)⒍际侵悄苤羔?,?huì)被自動(dòng)回收,通常不可能出現(xiàn)內(nèi)存泄漏。但C++標(biāo)準(zhǔn)沒有強(qiáng)制規(guī)定函數(shù)參數(shù)的計(jì)算順序,不同的編譯器處理不同。編譯器可能將上面代碼細(xì)化操作為:對(duì)第一個(gè)參數(shù)執(zhí)行newWidget,分配內(nèi)存對(duì)第二個(gè)參數(shù)執(zhí)行newWidget,分配內(nèi)存對(duì)第一個(gè)參數(shù)執(zhí)行構(gòu)造函數(shù),實(shí)例化對(duì)第二個(gè)參數(shù)執(zhí)行構(gòu)造函數(shù),實(shí)例化如果第3步失敗,拋出異常,第1步申請(qǐng)的內(nèi)存被自動(dòng)回收,但第2步分配的內(nèi)存就丟失了【糾正措施】修改代碼為:shared_ptr<Widget>sp1(newWidget);shared_ptr<Widget>sp2(newWidget);Fun(sp1,sp2);【舉一反三】不要在一條語句中分配一個(gè)以上的資源,應(yīng)該在自己的代碼語句中這些顯式的資源分配(比如new),而且每次都應(yīng)該馬上將分配的資源賦予管理對(duì)象(比如shared_ptr)。非空指針作為左值:需要釋放的指針參與運(yùn)算【問題描述】網(wǎng)上問題案例:while(NULL!=(lpSrcStr=strchr(lpSrcStr,'.'))){lpBackStr=lpSrcStr+1;
……if((lpCheck<0)||(lpCheck>255)){free(lpSrcStr);return0;}else{ulResult=(ulResult<<8)+lpCheck;
lpSrcStr=lpBackStr;//lpSrcStr指針發(fā)生了變化!
lpForStr=lpSrcStr;}}free(lpSrcStr);
【問題定位】lpSrcStr參與運(yùn)算后,指向的地址已經(jīng)不是最初使用malloc申請(qǐng)的內(nèi)存了,free(lpSrcStr)失敗【舉一反三】使用臨時(shí)變量參與指針運(yùn)算,確保原指針不會(huì)被修改。需要釋放的指針禁止參與運(yùn)算缺少分支處理【問題描述】網(wǎng)上問題案例:pMsg=g_InstantQueue.at(j);mu=SortOperation(pMsg);if(mu<=0){ ret=0;}elseif(mu<=5){ ret=SendMsgToMu(pMsg,mu-1);}if(ret<=0){ g_InstantQueue.removeAt(j);}【問題定位】mu<=0的分支沒有刪除消息,內(nèi)存泄漏;mu>5的分支,根本沒有考慮,內(nèi)存泄漏【舉一反三】if條件的所有分支都必須考慮清楚沒有釋放隊(duì)列中的內(nèi)存【問題描述】前面同一個(gè)案例:pMsg=g_InstantQueue.at(j);mu=SortOperation(pMsg);if(mu<=0){ ret=0;}elseif(mu<=5){ ret=SendMsgToMu(pMsg,mu-1);}if(ret<=0){
g_InstantQueue.removeAt(j);}【問題定位】如果SendMsgToMu失敗(ret<=0),則調(diào)用g_InstantQueue.removeAt(j)從消息隊(duì)列中移走消息,但并沒有釋放消息申請(qǐng)的內(nèi)存【舉一反三】處理鏈表或隊(duì)列時(shí),如果刪除了其中的一個(gè)節(jié)點(diǎn),還必須同時(shí)釋放節(jié)點(diǎn)申請(qǐng)的內(nèi)存
申請(qǐng)內(nèi)存以后才進(jìn)行異常檢查【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:NEW(pMsg,TMsg)……………..iFileCount++;if(iFileCount>100){break;}if(True==cacheAlarm(pMsgPara->m_alarmReport)){DELETE(pMsg);}else{sendMessage(pMsg);//把消息掛入消息隊(duì)列中}【問題定位】條件if(iFileCount>100)為真,導(dǎo)致break時(shí),內(nèi)存泄漏。
【糾正措施】將申請(qǐng)內(nèi)存的語句NEW(pMsg,TMsg)移動(dòng)到判斷語句if(iFileCount>100)后面【舉一反三】如果在程序塊中存在異常檢查或退出機(jī)制,應(yīng)放在最前面,這條控制語句前面的代碼必須與異常檢查或退出條件相關(guān)?;愔袥]有定義虛析構(gòu)函數(shù)【問題描述】代碼飛檢人員發(fā)現(xiàn)某產(chǎn)品缺陷跟蹤庫中這樣解決某網(wǎng)上問題:classA{//virtual~A(){}//問題定位:析構(gòu)函數(shù)中打印Log信息內(nèi)存越界,刪除析構(gòu)函數(shù)}【問題定位】檢查ClassA的子類:classB:publicA{
~B(){……};//子類B定義了自己的析構(gòu)函數(shù)};
如果出現(xiàn)下面的代碼:A*p=newB;
deletep;聲明的對(duì)象p的類型為基類A,但實(shí)際類型為子類B,所以deletep時(shí),編譯器認(rèn)為刪除的是ClassA的對(duì)象,則調(diào)用ClassA的析構(gòu)函數(shù)(編譯器對(duì)A添加的默認(rèn)析構(gòu)函數(shù)),而不調(diào)用B的析構(gòu)函數(shù),這樣B里面申請(qǐng)的內(nèi)存就泄漏了【糾正措施】在基類A中增加一個(gè)空的虛析構(gòu)函數(shù),則編譯器會(huì)自動(dòng)調(diào)用B的析構(gòu)函數(shù)【舉一反三】只要在類中有虛函數(shù),就必須有虛析構(gòu)函數(shù)基類中沒有定義虛析構(gòu)函數(shù)(續(xù))【問題描述】使用purify發(fā)現(xiàn)如下代碼內(nèi)存泄漏:voidCAlarmer::destroyAlarmInfo(IAlarmInfo*pAlarmInfo)DELETE(pCAI);}【問題定位】為了減少耦合,對(duì)外暴露的是IAlarmInfo的純虛接口。但內(nèi)部的實(shí)現(xiàn)是CAlarmInfo。在創(chuàng)建時(shí)創(chuàng)建一個(gè)CAlarmInfo而后強(qiáng)制轉(zhuǎn)換成IAlarmInfo返回。典型的使用基類指針進(jìn)行刪除,由于基類沒有設(shè)置虛析構(gòu)函數(shù),導(dǎo)致派生類的析構(gòu)函數(shù)沒有調(diào)用,這樣派生類CAlarmInfo里面申請(qǐng)的內(nèi)存就泄漏了?!炯m正措施】在基類IAlarmInfo中析構(gòu)函數(shù)設(shè)置為虛【舉一反三】允許通過基類的指針進(jìn)行刪除操作,則基類的析構(gòu)函數(shù)必須是公用且虛擬。未及時(shí)加入列表,異常拋出時(shí)內(nèi)存泄漏【問題描述】CPArray類為管理對(duì)象指針的列表,具有自動(dòng)釋放功能。程序正常情況下,加入CPArray中的對(duì)象可以得到釋放,在有異常發(fā)生的時(shí)候,對(duì)象的不到釋放,造成內(nèi)存泄漏?!締栴}定位】分析下面的代碼段,發(fā)現(xiàn)new的pObj已經(jīng)放到了自動(dòng)釋放列表中,粗看代碼沒什么問題;...CPArraymyArray(AUTO_FREE);CMyObject*pObj=NULL;for(inti=0;i<N;i++){pObj=newCMyObject;pObj->m_ID=i;...Function1(pObj);
...myArray.Add(pObj);}...跟蹤Function1函數(shù),在該函數(shù)內(nèi)有異常拋出,導(dǎo)致myArray.Add(pObj)語句沒有被執(zhí)行,造成pObj對(duì)象沒有被釋放。未及時(shí)加入列表,異常拋出時(shí)內(nèi)存泄漏(續(xù))【糾正措施】對(duì)象創(chuàng)建以后,首先加入到自動(dòng)釋放列表中,再執(zhí)行其他操作;以上代碼修改為:...CPArraymyArray(AUTO_FREE);CMyObject*pObj=NULL;for(inti=0;i<N;i++){pObj=newCMyObject;myArray.Add(pObj);pObj->m_ID=i;...Function1(pObj);
...}...無論函數(shù)Function1()是否有異常,都能保證對(duì)象得到釋放?!九e一反三】使用自動(dòng)釋放列表的程序,要第一時(shí)間將新創(chuàng)建對(duì)象加入到列表中;調(diào)用函數(shù)時(shí),要考慮到函數(shù)是否會(huì)拋出異常。沒有釋放隊(duì)列中的內(nèi)存【問題描述】發(fā)現(xiàn)加載**軟件命令出現(xiàn)內(nèi)存泄漏現(xiàn)象?!締栴}定位】分析下面的代碼段,發(fā)現(xiàn)刪除隊(duì)列節(jié)點(diǎn)時(shí),沒有刪除節(jié)點(diǎn)對(duì)應(yīng)的內(nèi)存。。。。。。。。。。。。。。//獲得加載命令附加信息指針CDldAllSwAddInfo*pDldAllSwInfo=(CDldAllSwAddInfo*)(g_TaskId.GetDataPtr(iTaskId));......//清除已經(jīng)完成加載的單板信息pDldAllSwInfo->m_pBoardReportList.RemoveAt(0);pDldAllSwInfo->m_pBoardReportList.SetSize(iSize-1);pDldAllSwInfo->m_pBoardReportList.FreeExtra();沒有釋放隊(duì)列中的內(nèi)存(續(xù))【糾正措施】添加如下代碼,問題解決:if(pDldAllSwInfo->m_pBoardReportList[0]!=NULL){deletepDldAllSwInfo->m_pBoardReportList[0];pDldAllSwInfo->m_pBoardReportList[0]=NULL;}pDldAllSwInfo->m_pBoardReportList.RemoveAt(0);pDldAllSwInfo->m_pBoardReportList.SetSize(iSize-1);pDldAllSwInfo->m_pBoardReportList.FreeExtra();【舉一反三】處理鏈表或隊(duì)列時(shí),如果刪除了其中的一個(gè)節(jié)點(diǎn),還必須同時(shí)釋放節(jié)點(diǎn)申請(qǐng)的內(nèi)存重復(fù)連接數(shù)據(jù)庫【問題描述】B020版本中,對(duì)****模塊進(jìn)行系統(tǒng)測試。某個(gè)VOD處于上載中,點(diǎn)擊<Refresh>,刷新其狀態(tài),則頁面運(yùn)行非常慢,最終出現(xiàn)錯(cuò)誤提示頁面?!締栴}定位】在****方法的synchronizeCAStatus(ArrayList)方法中,在循環(huán)體內(nèi)newOperater(),即在循環(huán)體中創(chuàng)建數(shù)據(jù)庫連接try{for(intindex=0;index<vodCAList.size();index++)
{dbOper=newDBOperator();。。。。。。。。。。。。。。
}}catch(Exceptione){。。。。。。。。。。。。。。}finally{dbOper.close();}重復(fù)連接數(shù)據(jù)庫(續(xù))【糾正措施】數(shù)據(jù)庫連接不能放在循環(huán)體中創(chuàng)建,否則會(huì)導(dǎo)致內(nèi)存泄漏,數(shù)據(jù)庫連接異常。將dbOper=newDBOperator();語句提前到循環(huán)體外,try語句內(nèi)try{dbOper=newDBOperator();for(intindex=0;index<vodCAList.size();index++)
{。。。。。。。。。。。。。。
}}catch(Exceptione){。。。。。。。。。。。。。。}finally{dbOper.close();}對(duì)非POD對(duì)象進(jìn)行memset操作【說明】POD(PlainOldData)是普通舊式數(shù)據(jù),如結(jié)構(gòu)/枚舉/成員指針等,內(nèi)存字節(jié)是連續(xù)的。非POD類的內(nèi)存字節(jié)不連續(xù),中間可能包含vptr(虛函數(shù)指針表)等隱藏的數(shù)據(jù)【問題描述】網(wǎng)上問題案例shared_ptr<int>p1(newint),p2(newint);memcpy(&p1,&p2,sizeof(p1);【問題定位】內(nèi)存泄漏:p2不會(huì)刪除;內(nèi)存訪問失?。簆1刪除兩次【舉一反三】C++編譯器經(jīng)常會(huì)在多態(tài)對(duì)象中嵌入一些隱藏?cái)?shù)據(jù)(如vptr),多重繼承/虛擬繼承會(huì)添加更多的內(nèi)部指針。不要直接對(duì)C++的類進(jìn)行memset/memcpy等操作,應(yīng)盡量使用類自身提供的賦值/復(fù)制功能。強(qiáng)制關(guān)閉線程:沒有釋放線程占據(jù)的資源【問題描述】網(wǎng)上問題案例CShakeHand::CShakeHand(){ m_hdShakeThreadrecv=CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)ThreadProc_ShakeHands, this,NULL,&m_ulShakeThreadID);}CShakeHand::~CShakeHand(){
TerminateThread(m_hdShakeThreadrecv,0);//強(qiáng)制關(guān)閉
CloseHandle(m_hdShakeThreadrecv);}【問題定位】線程被強(qiáng)制關(guān)閉,導(dǎo)致線程內(nèi)部資源/內(nèi)存泄漏【舉一反三】使用事件或信號(hào)量通知線程,確保線程調(diào)用自身的退出函數(shù)預(yù)防內(nèi)存泄漏的方法:RAIIRAII是英文ResourceAcquisitionIsInitialization的縮寫。意為在對(duì)象構(gòu)造時(shí)獲取資源,接著控制對(duì)資源的訪問使之在對(duì)象的生命周期內(nèi)始終有效,最后在對(duì)象析構(gòu)的時(shí)候釋放資源。RAII有兩大好處:不需要顯式地釋放資源。即使是使用資源的方法的控制結(jié)構(gòu)發(fā)生了改變,例如在中間插入return或者拋出異常,我們能確定資源肯定會(huì)在對(duì)象析構(gòu)的時(shí)候被釋放。對(duì)象所需的資源在其生命期內(nèi)始終保持有效——
我們可以說,此時(shí)這個(gè)類維護(hù)了一個(gè)invariant,通過該對(duì)象使用資源時(shí),不必檢查資源有效性,可以簡化邏輯。我司有的項(xiàng)目組規(guī)定,禁止在函數(shù)中間退出,必須goto到函數(shù)結(jié)尾;在函數(shù)結(jié)尾統(tǒng)一釋放所有的資源。這是在C語言中變通實(shí)現(xiàn)RAII的方法,這種情況下允許使用goto語句。代碼飛檢發(fā)現(xiàn),很多產(chǎn)品之所以被檢查出大量的內(nèi)存泄漏問題,主要原因是釋放內(nèi)存的規(guī)則混亂。有的函數(shù)自己申請(qǐng)內(nèi)存,自己釋放;有的申請(qǐng)后,傳入子程序中,子程序使用并釋放;有的由子程序申請(qǐng)內(nèi)存并返回父程序,層層使用后在某一個(gè)函數(shù)內(nèi)釋放。華為公司編程規(guī)范要求
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年北京景山學(xué)校通州分校擬招聘中小學(xué)教師備考題庫及答案詳解參考
- 數(shù)字化轉(zhuǎn)型下管理會(huì)計(jì)的合規(guī)應(yīng)用-政策遵循與價(jià)值創(chuàng)造研究畢業(yè)論文答辯
- 中小企業(yè)稅務(wù)籌劃的財(cái)務(wù)管理-合規(guī)前提與效益最優(yōu)雙保障答辯匯報(bào)
- 直播電商售后服務(wù)優(yōu)化-客戶滿意度與復(fù)購意愿關(guān)聯(lián)研究畢業(yè)論文答辯
- 績效工資協(xié)議合同
- 代收轉(zhuǎn)讓協(xié)議書
- 代訂門票協(xié)議書
- 代客外匯協(xié)議書
- 編制服務(wù)合同范本
- 簽訂解除合同協(xié)議
- 危重癥患者體溫管理課件
- 033《知識(shí)產(chǎn)權(quán)法》電大期末考試題庫及答案
- 中醫(yī)消防安全知識(shí)培訓(xùn)課件
- 多發(fā)性骨髓瘤的個(gè)案護(hù)理
- 洗胃操作并發(fā)癥及預(yù)防
- 貨運(yùn)托盤利用方案(3篇)
- 綠色建筑可行性分析報(bào)告
- 重癥超聲在ECMO治療中的應(yīng)用
- 2024年新人教版道德與法治一年級(jí)上冊(cè) 7 上課了好好學(xué) 教學(xué)課件
- 計(jì)算生物學(xué)試題及答案
- DB31/T 1108-2018監(jiān)護(hù)型救護(hù)車配置規(guī)范
評(píng)論
0/150
提交評(píng)論