久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    C++內(nèi)存泄漏檢查心得

     firefox_zyw 2013-09-04

    溫輝敏(http://blog.csdn.net/wenhm/article/details/mailto:wenhm@sina.com) 2009-7-1

    摘要:本文簡單介紹了C++編程時,大家經(jīng)常犯得一些內(nèi)存泄漏方面的編碼錯誤,并給出簡單的代碼示例。并簡要給出了Win32平臺下使用檢測內(nèi)存泄漏利器DevPartner BoundsChecker進(jìn)行檢查以發(fā)現(xiàn)泄漏代碼的詳細(xì)步驟。值此黨的節(jié)日,希望對一些迷失在內(nèi)存泄漏中的同志們有所幫助避免少走彎路。我一直覺得黨的黨章是完美的,原則是好的,共產(chǎn)主義社會肯定比資本主義財富集中在少數(shù)人手里強(qiáng),只是到了下面執(zhí)行就有所欠缺了,這次上海閔行封頂房的倒塌正是沒有一個良好監(jiān)督機(jī)制的問題,官員參股房地產(chǎn)明顯違背政府、法律,希望祖國越來越美好。

    作者博客:http://blog.csdn.net/wenhm/

     

    閑話少說,切入正題,本人在參與一個大型Win32軟件項目時,對整個項目進(jìn)行了內(nèi)存泄漏方面的檢查,隨著泄漏代碼的一個個發(fā)現(xiàn),發(fā)現(xiàn)許多的泄漏都具有某些共同性,于是乎總結(jié)了一些常見泄漏代碼,發(fā)給同事們看了。希望能提醒下大家,但后來在項目的二期、三期版本出現(xiàn)的泄漏發(fā)現(xiàn)和以前的一些問題幾乎是同一性質(zhì)的,回過來看以前寫的一些筆記發(fā)現(xiàn)都概括了(當(dāng)時覺得自己是有那么兩把刷子,^_^)。于是想到也許很多編程同志也會碰到同樣的問題,能寫下來放到網(wǎng)上起到拋磚引玉的作用就更好了。

     

    這里總結(jié)下檢測出來的內(nèi)存泄漏有共性的問題,希望對大家以后編程避免內(nèi)存泄漏有所幫助:

    1.  類內(nèi)成員動態(tài)分配

    類所有動態(tài)分配的成員變量,一定記得在析構(gòu)函數(shù)中全部進(jìn)行判斷釋放內(nèi)存。當(dāng)類中有指針成員變量,很多人喜歡在構(gòu)造函數(shù)中來動態(tài)分配初始化指針變量但常常忘了在析構(gòu)函數(shù)中來釋放內(nèi)存。當(dāng)你初來人世,父母疼愛你,當(dāng)你有了自己的小孩,你又有了生活的重心,此時父母已經(jīng)被你遺忘了,當(dāng)你的小孩又有了自己的小孩,你也就被遺忘了。我們很多程序員同志也是這樣的,使用時我要用,new得挺爽的,用完不管我事了,這明顯是違反我黨一貫有始有終的原則的。

    示例代碼:

    class CApple

    {

    public:

           CApple()

           {

                  m_ptrData = new char[128];

    }

     

    ~CApple()

    {

    }

    }

    上面的m_ptrData指向的內(nèi)存就這樣泄漏掉了,記得在析構(gòu)函數(shù)中加上釋放的代碼,改為如下:

    ~CApple()

    {

            if(NULL != m_ptrData)

    {

           delete m_ptrData;

           m_ptrData = NULL;

    }

    }

    需要提醒的是:上面刪除m_ptrData再置NULL,是一個良好的編程習(xí)慣,可以避免產(chǎn)生野指針。(當(dāng)然這里對象都析構(gòu)了不存在這個問題,但其它很多地方將刪除的指針置NULL是非常明智的一個做法,不然鬼知道這個指針指向的內(nèi)存是否是有效的)

    2.     指針容器

    std::vector<CType>這個錯誤也是同志們經(jīng)常犯的,其實很多時候若是簡單結(jié)構(gòu)、簡單類,你直接用std::vector<CType>就好了,能不用std::vector<CType*>就盡量不用,因為確實很容易忘了vector中原來還存放了要釋放的內(nèi)存的指針,而且在clear或是刪除一個元素時都得記起來釋放指針指向的內(nèi)容。

    這個很像小時候家里收郵包,郵遞員不將郵包送到家里來,也許因為太沉了吧,只是給張包裹單要自己取領(lǐng)。今天忙,往抽屜里一扔,然后就忘了,下次又來一包裹單,又往抽屜里一扔又忘了(指針壓入vector),若你不小心將包裹單(指針)弄丟了,你自己都不知道有這么回事(忘了釋放內(nèi)存)。但現(xiàn)在就好多了,快遞公司包裹直接送到你手上。

    示例代碼就不提供了,只能意會不能言傳,^_^

     

    3.     指針賦值

    若不是在定義指針代碼作用范圍內(nèi),使用其它地方定義的指針時(比如全局指針,類成員變量指針),進(jìn)行賦值操作的時候先判斷原來指針是否有值,有則先釋放原來的內(nèi)存。

    因為若指針原來有值的話,你一覆蓋原來分配的內(nèi)存就再也找不到了,也就產(chǎn)生了泄漏。

    代碼示例:

    void CMainModule::BulidList()

    {

         m_ptrList = new CList;

         .

    }

    上面的代碼,若BuildList跑到第二次時就會出問題了,此時m_ptrList本來就已經(jīng)指向一塊動態(tài)分配的內(nèi)存了,你這時不分青紅皂白再new一塊賦值過去就將前面動態(tài)分配的內(nèi)存給丟失了。

    此時應(yīng)該先判斷m_ptrList是否為NULL,為NULLnew一塊內(nèi)存,否則就應(yīng)考慮重用原來的內(nèi)存或是先刪除原來再new

     

    4.     掃尾函數(shù)

    有些類型對象如CDialogCWindowCFileCImage等需要在Delete前做CloseReleaseDestroy等操作的,Delete時檢查是否已經(jīng)調(diào)用了相應(yīng)的掃尾函數(shù)。

    這個要具體情況具體分析了,比如CDialog的子類銷毀時往往需要先調(diào)用OnDestroy或是DestroyWindow,不然就可能會存在資源泄漏的問題。

     

    5.     公共模塊/第三方庫

    公共模塊一般有init()open()release()terminate()close()兩種類型的函數(shù),不要忘記掃尾類型函數(shù)的調(diào)用。

    在我們這個軟件項目中就有用到一個第三方的Av.dll,主要是進(jìn)行視頻編解碼方面的庫,這個庫需要進(jìn)行初始化才能用,同時也提供了使用完關(guān)閉的方法。當(dāng)時一位同志就忘了調(diào)用掃尾函數(shù)導(dǎo)致了大量的內(nèi)存泄漏。這個就要求我們使用第三方庫時一定要看仔細(xì)使用說明,不要一味冒進(jìn)。

     

    6.     異常分支

    若正常分支有內(nèi)存需要釋放,則不要忘了異常分支的內(nèi)存釋放如try語句的catch分支,函數(shù)中的多個return分支都要考慮到相應(yīng)內(nèi)存的釋放。

    示例代碼:

    try

    {

           void *ptrData = new char[128];

     

           /// do something …

           ….

           if(NULL  !=  ptrData)

    {

           delete ptrData;

           ptrData = NULL;

    }

    }

    catch(CException &e)

    {

           LOG(LOG_LEVEL_ERROR, " errorcode:" << e.errorCode());

    }

    catch(…)

    {

           LOG(LOG_LEVEL_ERROR, " errorcode:");

    }

     

    上面的代碼就沒有考慮到兩個異常分支也應(yīng)該要判斷指針是否要進(jìn)行釋放的情況。當(dāng)跑到異常分支中去時就產(chǎn)生了內(nèi)存泄漏了,這種問題比較難查因為正常情況下程序也是正常不會有泄漏的,能編寫代碼時就注意就事半功倍了。

     

    7.     動態(tài)分配對象數(shù)組:

    動態(tài)分配的對象數(shù)組,記得使用delete[]來進(jìn)行刪除。基于兩個考慮:

    (1)可以釋放整個數(shù)組的空間;

    (2)調(diào)用數(shù)組中每個對象的析構(gòu)函數(shù)。

     

    第一個其實使用delete加上數(shù)組地址一樣是可以釋放的,因為這塊內(nèi)存是連續(xù)分配的,不論采用delete或是delete[]來釋放,操作系統(tǒng)都能將這塊連續(xù)的內(nèi)存一起釋放掉。

    但第二點有什么作用呢,此時大家看看 第一章類內(nèi)成員動態(tài)分配 中的示例就知道了,很多釋放內(nèi)存的代碼是放在類的析構(gòu)函數(shù)中的,只有使用delete[]才能正確調(diào)用析構(gòu)函數(shù)。使用delete是不會調(diào)用每個數(shù)組元素的析構(gòu)函數(shù)的。

     

    8.     非常規(guī)動態(tài)內(nèi)存分配

    不是采用常規(guī)內(nèi)存分配(newmalloccallocrealloc)的內(nèi)存也要記得釋放,如strdup等。

    有一些C/C++ Api返回的指針是動態(tài)分配的需要使用者來負(fù)責(zé)釋放,這個只要使用時看清楚Api的說明就不會有什么問題了。

     

    9.     單態(tài)模式

    最好在程序退出時釋放內(nèi)存,雖然OS會回收,但對于我們以后內(nèi)存泄漏檢測工作能帶來極大方便。

    雖然單態(tài)模式的內(nèi)存泄漏是一次性泄漏,不會導(dǎo)致內(nèi)存的不斷增加,但因為很多內(nèi)存泄漏檢查工具都是程序正常結(jié)束后開始統(tǒng)計內(nèi)存泄漏的,此時會將單態(tài)模式的內(nèi)存泄漏也統(tǒng)計進(jìn)去。這樣我們就得一個個區(qū)分那個是單態(tài)泄漏那個是非法泄漏,會帶來很大的工作量,若能在程序退出時將單態(tài)模式的內(nèi)存泄漏也釋放掉,檢測結(jié)果就會集中在有問題的內(nèi)存泄漏上了,大大減少我們的工作量。

    解決方法:

    為單態(tài)模式對象定義DestroyInstance()方法用來釋放單態(tài)模式的內(nèi)存,在程序退出時調(diào)用該函數(shù)。

    或是采用static smart 指針來讓編譯器自動在程序退出時負(fù)責(zé)釋放相應(yīng)的內(nèi)存。

     

    10.             虛析構(gòu)函數(shù)

    一個類的指針被向上引用,作為基類的指針來使用的時候,把析構(gòu)函數(shù)寫成虛函數(shù)。這樣做是為了當(dāng)用一個基類的指針類型來刪除一個派生類的對象時,派生類的析構(gòu)函數(shù)會被調(diào)用。(new子類的對象,刪除時卻采用delete父類類型的指針。new CConcreteClass的對象ptr,但delete CClass類型 的指針ptr,無法調(diào)用正確的析構(gòu)函數(shù))

    當(dāng)針對接口進(jìn)行編程時,涉及到動態(tài)分配的對象指針在各函數(shù)間傳遞時特別要注意將基類的析構(gòu)函數(shù)定義成虛函數(shù)。

    第一章提到了,若沒有正確的調(diào)用析構(gòu)函數(shù),析構(gòu)函數(shù)中若有釋放內(nèi)存的代碼就會得不到運(yùn)行,而且本具體子類中的一些成員變量的析構(gòu)函數(shù)也得不到執(zhí)行。因為編譯器會認(rèn)為你刪除的是一個基類類型的指針,當(dāng)然就不會去調(diào)用子類的成員變量的析構(gòu)函數(shù)的了。

    代碼示例:

    struct ST_Info

    {

          int iWeight;

          char strName[128]

    }

    class CFruit

    {

    };

     

    class CApple:public CFruit

    {

    public:

          std::vector< ST_Info> m_vecInfo;

    }

     

    CFruit * GetApple()

    {

       CApple *ptrApple = new CApple();

       ST_Info st_Info = {9, “Apple1”};

       ptrApple->m_vecInfo.push_back(st_Info);

     

       return ptrApple; 

    }

    void main(int argc, char**argv)

    {

       CFruit *ptrFruit = GetApple();

      

       delete ptrFruit;

       ptrFruit = NULL;

    }

     

    上面的代碼就會產(chǎn)生內(nèi)存泄漏了, ptrApple->m_vecInfo中存放的內(nèi)存將全部泄漏掉,一個能為delete時認(rèn)為這是一個CFruit *的指針,不會去釋放ptrApple->m_vecInfo中元素對應(yīng)的內(nèi)存。

    修正方法是只要將CFruit的析構(gòu)函數(shù)定義成虛析構(gòu)函數(shù)就OK了。

     

    11.             線程的安全退出,user-interface thread安全退出

    和窗口關(guān)聯(lián)的user-interface thread 必須處理WM_DESTROY消息,建議定義一個OnDestroy()函數(shù),該函數(shù)調(diào)用PostQuitMessage(0)的方法讓user-interface thread安全退出,防止線程不安全退出導(dǎo)致內(nèi)存泄漏。

    線程進(jìn)行安全退出,防止非正常退出的內(nèi)存泄漏問題。

    例子:

    LRESULT CMsgReflect::OnDestroy(HWND hWindow, UINT uiMessage, WPARAM uiParam, LPARAM ulParam)

    {

             PostQuitMessage(0);

             return 0;

    }

     

    12.             內(nèi)存動態(tài)分配后,在各個分支路徑均要考慮是否要釋放掉

    這個其實和第6章是類似的,下面的代碼就沒有考慮到執(zhí)行到continue時的情況會產(chǎn)生內(nèi)存泄漏。

                    for (std::vector<TeamInfo>::iterator it = e.teamlist.begin(); it != e.teamlist.end(); it++)

                    {

                            FriendGroupData *pGroup=new FriendGroupData;

                            if(it->unTeamID==DEFAULT_FRIEND_GROUP_ID)

                                continue;

                           .

                           delete pGroup;

    }

    附錄:DevPartner BoundsChecker的使用

    1.www .3ddown.com 網(wǎng)上可以下載到8.2版本的DevPartner,進(jìn)行安裝即可。

    2.License的下載和安裝,http://download.csdn.net/source/828960,運(yùn)行Distributed License Management,將該license導(dǎo)入即可。

    3.將系統(tǒng)時間改成2008年才能使用該license,此時就可以進(jìn)行內(nèi)存泄漏的檢測了,記得檢測完將系統(tǒng)時間改回來就OK了。

    4.調(diào)整跟蹤堆棧的深度,在Visual Studio界面中,DevPartner->Options,然后

    Error Detection->Data Collection 即可調(diào)整跟蹤堆棧的深度了。

     

     

    參考資料:

    1.內(nèi)存泄漏的檢測、定位和解決經(jīng)驗總結(jié)》,http://blog.csdn.net/wenhm/archive/2006/06/11/787876.aspx

     

      本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評論

      發(fā)表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 377P欧洲日本亚洲大胆| 国产精品大全中文字幕| 中文字幕日韩国产精品| 国产成人无码A区在线观| 丁香色欲久久久久久综合网| 777奇米四色成人影视色区| 国产成熟妇女性视频电影| A级大胆欧美人体大胆666| 亚洲欧美高清在线精品一区二区 | 亚洲香蕉网久久综合影视| 又色又爽又黄的视频网站| 色欲综合久久中文字幕网| 国产线观看免费观看| 婷婷综合久久狠狠色成人网| 怡红院一区二区三区在线| 无码国产精品一区二区免费模式 | xxxx丰满少妇高潮| 欧美亚洲日本国产综合在线美利坚| 久久亚洲国产精品成人AV秋霞| 亚洲人成电影在线天堂色| 亚洲AV无码专区亚洲AV| 国产在线一区二区不卡| 深夜国产成人福利在线观看| 国产亚洲精品VA片在线播放| 人妻少妇精品中文字幕| 国产欧美成人XXX视频| AV在线亚洲欧洲日产一区二区| 国产成人综合95精品视频| 真实国产老熟女粗口对白| 亚洲一二三区精品美妇| 久久久国产精品VA麻豆| 亚洲香蕉网久久综合影视| 久久精品国产亚洲AV高清热| 国内精品伊人久久久久影院对白 | 国产欧美一区二区精品久久久| 毛片大全真人在线| 视频一区视频二区制服丝袜 | 国产一区二区高清不卡| 精品欧美一区二区在线观看| 国产线播放免费人成视频播放| 又黄又爽又无遮挡免费的网站|