• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • Visual C++及C++中的智能指針應用分析

    發表于:2007-04-27來源:作者:點擊數: 標簽:指針C++應用智能visual
    前段時間,在查控件的內存泄露時,最終找出一個錯誤:在使用XMLDom(COM)時,由于重復使用某接口指針前未釋放Dispatch指針(Release),而導致內存泄露,而此類錯誤(如同BSTR類型的泄漏),VC的調試器和Bondcheck均無能為力。解決辦法,似乎只有細心一途。 但只
      前段時間,在查控件的內存泄露時,最終找出一個錯誤:在使用XMLDom(COM)時,由于重復使用某接口指針前未釋放Dispatch指針(Release),而導致內存泄露,而此類錯誤(如同BSTR類型的泄漏),VC的調試器和Bondcheck均無能為力。解決辦法,似乎只有細心一途。

      但只要稍稍仔細看看,就可發現,實際上如果正確使用VC提供的智能指針,是可以避免此問題的。

      另外,一直為Java程序員津津樂道的內存使用無需管理的優勢,一直知道用C++的智能指針可以模擬。但一直沒實際動手做過,趁此分析之機,用C++簡單包裝了一個。反正粗看之下,可以達到與Java類似的效果,當然,C++的對象更高效且節省內存。

      就以上所提到的,時間關系,我只能簡單羅列幾點,代碼應該是正確的(但未檢查)。前后文沒什么邏輯關系,但如果要進一步應用C++的智能指針,相信會起到拋磚引玉之效。

      一:關于糾錯,MFC和ATL中智能指針的應用

      1:在Windows中如何方便的查看當前進程使用的內存。

      雖然代碼簡單,但對糾錯時有大用處,不用不停的通過切換任務管理器來查看內存使用。代碼如下:

    clearcase/" target="_blank" >cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
    UINT C_BaseUtil::getProcessMemoryUsed()
    {
     UINT uiTotal = 0L;
     HANDLE hProcess = ::GetCurrentProcess();
     PROCESS_MEMORY_COUNTERS pmc;
     if(::GetProcessMemoryInfo(hProcess,&pmc,sizeof(pmc)))
      uiTotal = pmc.WorkingSetSize;
     return uiTotal;
    }

      注意:由于內存使用會是一個不穩定的過程,所以,需要在程序穩定時進行調用,才能準確。

      2:在使用Com的Dispatch指針時,如果不使用COM智能指針,容易出現的錯誤。

      2.1:忘記在所有出口釋放指針。
      
      如:

    IXMLDOMDocument *pDoc = NULL;
    CoCreateInstance(...)
    ……
    pDoc->Release();

      錯誤:如果中間代碼發生異常,則pDoc未能正常釋放,造成內存泄露。

      2.2:重復使用同一指針變量,導致中間生成的Dispatch指針未能釋放。

    IXMLDOMNode *pNode = NULL;
    if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &pNode)) || pNode==NULL)
    throw(_T("selectSingleNode failed!"));
    if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &pNode)) || pNode==NULL)
    throw(_T("selectSingleNode failed!"));

      錯誤:pNode未釋放就開始第二次調用,造成內存泄露?;蛘哳愃苝Node = pNode2的這種寫法,也隨手就出問題了。必須調用if(pNode) {pNode->Release();pNode=NULL;}

      3:使用MFC提供的Com智能指針解決上述問題。

      注意:可通過查看源碼,看到#import生成的智能指針的原型是_com_ptr_t。

      3.1:

    IXMLDOMDocumentPtr docPtr = NULL;
    docPtr.CreateInstance(...)
    ……

      這下不會有問題了,因為docPtr在析構時會有正確的釋放處理。

      3.2:

    IXMLDOMNodePtr nodePtr = NULL;
    if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &nodePtr)) || nodePtr==NULL)
    throw(_T("selectSingleNode failed!"));
    if(FAILED(pDoc->selectSingleNode(_bstr_t("Workbook"), &nodePtr)) || nodePtr==NULL)
    throw(_T("selectSingleNode failed!"));

      不會出錯了,因為_com_ptr_t重載了&操作符,在取指針時,有如下操作,嘿。

    Interface** operator&() throw()
    {
     _Release();
     m_pInterface = NULL;
     return &m_pInterface;
    }

      3.3: nodePtr = nodePrt2 ,也不會有問題:

      仔細查看源碼,在=操作符中會調用Attach,而Attach的做法是:會先調用_Release();

      3.4:再看看值傳遞:拷貝構造函數如下

    template<> _com_ptr_t(const _com_ptr_t& cp) throw()
    : m_pInterface(cp.m_pInterface)
    {
     _AddRef();
    }

      嗯,也不會有問題。

      3.5:最后我們也總結一下使用COM智能指針時的注意事項:

      ·不要在Com智能指針的生命期如果在::CoUninitailize之后,那請在調用::CoUninitailize之前,強制調用MyComPtr = NULL;達到強制釋放的目的。否則會出錯。

      ·不要混用智能指針和普通Dispatch指針,不要調用MyComPtr->Release(),這違背智能指針的原意,會在析構時報錯。

      4:使用ATL提供智能指針:CComPtr或是CComQIPtr.

      如果不使用MFC框架,要自已包裝IDispatch,生成智能指針,還可以使用ATL提供的智能指針。查看源碼,并參照《深入解析ATL》一書,發現實現與_com_ptr_t大同小異,效果一致。

      二:引申一下,我們來看看C++的智能指針

      1:說到智能指針,我們一定要看看標準C++提供的auto_ptr。而auto_ptr的使用是有很多限制的,我們一條一條來細數:

      1.1:auto_ptr要求一個對象只能有一個擁有者,嚴禁一物二主。

      比如以下用法是錯誤的。

    classA *pA = new classA;
    auto_ptr<classA> ptr1(pA);
    auto_ptr<classA> ptr2(pA);

      1.2:auto_ptr是不能以傳值方式進行傳遞的。

      因為所有權的轉移,會導致傳入的智能指針失去對指針的所有權。如果要傳遞,可以采用引用方式,利用const引用方式還可以避免程序內其它方式的所有權的轉移。就其所有權轉移的做法:可以查看auto_ptr的拷貝構造和=操作符的源碼,此處略。

      1.3:其它注意事項:

      ·不支持數組。

      ·注意其Release語意,它沒有引用計數,與com提供的智能指針不同。Release是指釋放出指針,即交出指針的所有權。

      ·auto_ptr在拷貝構造和=操作符時的特珠含義決定它不能做為STL標準容器的成員,

      好了,看了上面的注意事項,特別是第三條,基本上可以得出結論:在實際應用場合,auto_ptr基本沒什么應用價值的。

      2:如何得到支持容器的智能指針。

      我們利用auto_ptr的原型,制作一個引用計數的智能指針,則時讓它支持STL容器的標準。實現代碼很簡單,參照了《C++標準程序庫》中的代碼,關鍵代碼如下:

    template<class T>
    class CountedPtr {
     T * ptr;
     long * counter;
     public:
      //構造
      explicit CountedPtr(T* p = NULL)
      :ptr(p),count(new long(1){}
      //析構
      ~CountedPtr() {Release();}
      //拷貝構造
      CountedPtr(cont CountedPtr<T>& p)
      :ptr(p.ptr),count(p.count) {++*counter;}
      //=操作符
      CountedPtr<T>& operator= (const CountedPtr<T>& p) {
       if(this!=&p) {
        Release();
        ptr=p.ptr;
        counter=p.counter;++*counter;
       }
       return *this;
      }
      //其它略
      ....
     private:
      void Release() {
       if(--*counter == 0) {
        delete counter;
        delete ptr;
       }
      }
    }

      好了,這樣,當復制智能指針時,原指針與新指針副本都是有效的,這樣就可以應用于容器了?,F在,通過CountedPtr包裝的C++對象,是不是和Java的對象類似了呢,呵呵。只要再加上一些必要的操作符,它就可以作為容器中的共享資源來使用了。

    原文轉自:http://www.kjueaiud.com

    評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
    ...
    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>