C++ 智能指標

來源:互聯網
上載者:User

C++中的智能指標

 

簡單地講,智能指標是用一個對象來對指標進行建模,使之具有指標的特性,跟指標具有相同含義的->,*操作.並且通過對象的建構函式(擷取資源),析構資源(釋放資源)來對資源進行管理,從而減少程式員對通過new操作擷取到的對象的生命週期進行管理的負擔.根據《Moden C++ Design》, 我們可以構造具有很多正交特性的智能指標。 1.1  C++中的智能指標與JAVA中的對象前段時間跟朋友聊了些有關JAVA的東西,感覺上Java中的對象就是C++中的智能指標,但具有不同的資源釋放方式。在JAVA中,不能象C++中運用" A a;"語句聲明得到一個類(A)的案例a,而必須通過下列語句來獲得:Aa = new A.要在釋放a時,應用必需通知GC(垃圾收集功能)來釋放該執行個體所佔用的資源。當然,JAVA中的對象有一小點同C++中的職能不同,因為在C++中指標不具有"."操作符,故智能指標一般也不提供"."操作符,但在Java中都是通過"."操作符對對象進行操作的,不過我們可以把C++中智能指標的"->"操作符與Java中的"."操作符進行類比 1.2  引用計數型智能指標在C++中有一種常用的智能指標是引用計數型智能指標:RCSmartPtr. 它的實現基理如下:首先,存在RCObject,即存在一個對象,該對象提供引用計數介面。其次,要存在指向RCObject的RCSmartPtr對象,在RCSmartPtr對象的構造過程中,把指向RCObject的指標作為參數傳入RCSmartPtr中。因此每增加一個RCSmartPtr對象,就多了一個指向RCObject的指標。RCSmartPtr可以通過調用RCObject的引用計數介面,增加RCObject的引用計數。同樣的道理可以在RCSmartPtr對象的解構函式中調用RCObject的引用記數介面來減少RCObject的引用記數。最後,在對RCObject的引用計數進行操作時對引用計數進行檢查,如果引用計數為0,則RCObject將摧毀本身,從而釋放該對象所佔用的資源。通過這種方式,我們就可以把對資源的管理交給機器來管理,解除了對人工的倚賴。

  

轉載聲明: 本文轉自 http://www.cppblog.com/martin/archive/2009/03/03/martin_yahoo.html

==================================================================

C++中的智能指標應用分析

 

  前段時間,在查控制項的記憶體泄露時,最終找出一個錯誤:在使用XMLDom(COM)時,由於重複使用某介面指標前未釋放Dispatch指標(Release),而導致記憶體泄露,而此類錯誤(如同BSTR類型的泄漏),VC的調試器和Bondcheck均無能為力。解決辦法,似乎只有細心一途。

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

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

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

  一:關於錯誤修正,MFC和ATL中智能指標的應用

  1:在Windows中如何方便的查看當前進程使用的記憶體。

  雖然代碼簡單,但對錯誤修正時有大用處,不用不停的通過切換工作管理員來查看記憶體使用量。代碼如下:

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未釋放就開始第二次調用,造成記憶體泄露。或者類似pNode = 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;
   } 
  }
}

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

 

轉載聲明: 本文轉自 http://www.bccn.net/Article/kfyy/vc/jszl/200608/4310_2.html (編程中國)

推薦參考: android智能指標(sp wp)

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.