條款3:按照功能和實現原理選擇合適的智能指針
智能指針種類繁多,從實現原理上可以劃分為“基於所有權傳遞”和“基於引用計數”兩大類。C++中內置的std::auto_ptr則為“基於所有權傳遞”的智能指針。而Boost庫中的shared_ptr則是“基於引用技術”的智能指針(目前已經成為C++0x【6】的一部分)。
如“基於所有權傳遞”的智能指針會在將自己賦值給其他智能指針或者普通指針後將自己置空。這樣資源對象的權限被“移交”出去,使得每個資源在一個時候僅僅有一個擁有者持有其訪問權限。如std::auto_ptr便是這樣的指針:
view plaincopy to clipboardprint?auto_ptr<int> ap1(new int(0));
auto_ptr<int> ap2 = ap1;
cout<<*ap1; //錯誤,此時ap1只剩一個null指針在手了
auto_ptr<int> ap1(new int(0));
auto_ptr<int> ap2 = ap1;
cout<<*ap1; //錯誤,此時ap1只剩一個null指針在手了
這種智能指針簡單,且能盡早的發現錯誤(不像引用計數錯誤後導致難以查找的問題。)智能指針對資源的唯一所有權,使得其很明確的知道什麼時候需要對其釋放。
而缺點也是很明顯的:資源共享不方便。且無法將這類指針放置於STL這樣的標准類模版中【7】。因為STL參數傳遞都采用的是值傳遞,而非引用傳遞。試想一下,若放置一個智能指針在容器中,而後只是通過容器訪問了一下此指針,容器中的指針就指向NULL了,情況是多麼糟糕。
而“基於引用計數”的智能指針則會在智能指針內部維持一個引用計數。當產生一份智能指針的拷貝(如將一個智能指針賦值到另一個智能指針)則引用計數遞增一次,若智能指針出棧,則析構函數會讓引用計數遞減一次。若當引用計數為0則表明沒有任何智能指針再指向這個資源對象了,此時智能指針會對資源進行銷毀。
COM的智能指針都采用這種方式實現,因為COM本生就采用引用計數,而且需求上也要求COM能被多個指針訪問,這些指針都應當有COM組件的所有權。如下:
view plaincopy to clipboardprint?CComPtr<IX> spIX = NULL;
HRESULT hr = spIX.CoCreateInstance(CLSID_MYCOMPONENT);
If(SUCCEEDED(hr))
{
spIX->Fx();
CComPtr<IX> spIX2 = spIX;//賦值操作會使得原來的引用計數遞增。
spIX2->Fx();
spIX->Fx(); //賦值運算後,原來的智能指針還是能繼續使用。
}//智能指針析構的時候遞減其引用計數,並決定是否銷毀其所擁有的資源
CComPtr<IX> spIX = NULL;
HRESULT hr = spIX.CoCreateInstance(CLSID_MYCOMPONENT);
If(SUCCEEDED(hr))
{
spIX->Fx();
CComPtr<IX> spIX2 = spIX;//賦值操作會使得原來的引用計數遞增。
spIX2->Fx();
spIX->Fx(); //賦值運算後,原來的智能指針還是能繼續使用。
}//智能指針析構的時候遞減其引用計數,並決定是否銷毀其所擁有的資源
而按照功能上劃分,則可以用“COM的智能指針”、“內存管理智能指針”、“IO智能指針”等將智能指針劃分為不同類別。
如shared_ptr 和 CComPtr雖然從實現原理上說都是“基於引用技術”的。但卻會完成不同的功能,如:shard_ptr在析構時候發現引用計數為0時,會delete掉指針所指的那段內存。而CComPtr則是在析構時簡單的調用COM接口的Release()函數。引用計數和COM組件的銷毀工作都是由組件自身完成的。明白了這一點,你可能就不會輕易的將一個智能指針簡單的用在一個COM接口上了。
本位重點談論的是COM技術以及其智能指針,所以文章大部分內容只會用CComPtr和_com_ptr_t進行舉例。對其他類型的智能指針不加以更為詳細的論述,有興趣的讀者可以參看其他資料自行學習。
讓我們先來看一下這兩COM套指針的概述和,使用的一般方法。
作者“liuchang5的專欄”