程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 有效的使用和設計COM智能指針——條款2:引用計數的是與非

有效的使用和設計COM智能指針——條款2:引用計數的是與非

編輯:關於C語言

條款2:引用計數的是與非
使用COM做開發的程序員往往會被接口引用計數所帶來的問題搞得頭破血流。引用計數這個老大難問題存在的原因也相當簡單:在COM開發中,客戶僅僅知道組件的接口。當使用完一個接口而要使用另外一個接口時,由於客戶並不知道兩個接口是否指向同一組件,因此客戶無法直接將組件釋放。COM組件的生命周期也因此無法被客戶方便的控制。

於是為了解決這一問題,COM組建使用了引用計數。每個組件根據引用計數的數值決定何時釋放自身所占用的資源。當客戶從某個組件查詢出一個接口時,此引用計數值將增1。當客戶使用完此接口時,此引用值減1。如果某個組件的引用計數值降至0時候,組件則會將自己從內存中刪除。

通過引用計數確實可以很合理的管理組件的生命周期,但也嚴格要求開發人員遵循下面這三條簡單規則【1】:

1.在返回之前調用AddRef。對於那些返回接口指針的函數,在返回前應用相應的指針調用AddRef。這些函數包括QueryInterface及CreateInstance。這樣當客戶從這種函數得到一個接口後,他將無需調用AddRef。

2.使用完接口之後調用Release。在使用完某個接口之後應調用此接口的Release函數。

3.在賦值之後調用AddRef。將一個接口指針賦值給另外一個接口指針時,應調用AddRef。換句話說,在建立接口的另外一個引用之後應增加相應組件的引用計數。

根據如上三條原則我們需要編寫出了形如下例的代碼:

view plaincopy to clipboardprint?void SomeApp( IHello * pHello ) 

 
    IHello* pCopy = pHello; 
    pCopy->AddRef();  
    OtherApp(); 
    pCopy->Hello(); 
    pCopy->Release(); 

void SomeApp( IHello * pHello )
{

    IHello* pCopy = pHello;
    pCopy->AddRef();
    OtherApp();
    pCopy->Hello();
    pCopy->Release();
}

這三條規則看似簡單,但是程序員若在某一時刻遺漏掉其中的某條規則,則會使得引用計數陷入混亂。出錯後,最樂觀的情況是程序由於訪問了已經被釋放的資源,而直接崩潰。如果運氣不是特別好,那麼資源洩露則悄無聲息的發生了。

可以看出由於引用計數的引入,COM組件的生命周期可以自行管理,但同時也使得COM的使用變得非常危險。因為使用過程中需要每一個使用者都要嚴格並且正確的調用AddRef()和Release(),一旦出現問題,就會造成對象不能被正常釋放,或者對象被重復刪除,造成程序崩潰。所以使用COM接口,必須小心翼翼才行。

我們試著用智能指針改寫上述代碼。這裡以CComPtr為例,更多關於它的介紹我們放到後續條款中。上述函數將會變成如下這種形式:

view plaincopy to clipboardprint?void SomeApp( IHello * pHello ) 

    CComPtr<IHello> spHello= pHello; 
    OtherApp(); 
    spHello->Hello(); 
}  
void SomeApp( IHello * pHello )
{
    CComPtr<IHello> spHello= pHello;
    OtherApp();
    spHello->Hello();
}
 

智能指針給我帶來了引用計數的半自動化處理(之所一說是半自動化,是因為有些地方仍然需要手動管理引用計數)。AddRef()和Release()操作不見了,代碼簡潔了不少,而更重要的是它還將帶來更多的便利條件。我們在之後的條款中會進一步討論。

 作者“liuchang5的專欄”

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved