天天看點

有效的使用和設計COM智能指針——條款10:盡量減少智能指針和接口指針的混用

條款10:盡量減少智能指針和接口指針的混用

更多條款請前往原文出處:http://blog.csdn.net/liuchang5

在開始一節之前,讓我們先來看一個例子:

void func(void)
{
    ICalculator *pCalculator  = NULL;
    CComPtr<ICalculator>  pCalculator = NULL;
    hrRetCode = CoCreateInstance(  //在這裡建立COM元件,引用計數為1。
        CLSID_CALCULATOR,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_ICALCULATOR,
        (void **)&pCalculator
    );
    assert(hrRetCode);
    spCalculator = pCalculator;    //将建立好的元件交給智能指針讓其管理引用計數
                                   //指派運算符會再次調用AddRef()增加引用計數
    spCalculator->DoSomething();
}                             //調用Release()後引用計數不為0,發生了資源洩漏。
           

如果不仔細觀察,可能很難發現上述例子中存在一個資源洩漏問題。但細緻的分析一下,可以發現上述代碼中CoCreateInstance中會調用一次接口的AddRef()。而在智能指針指派過程中又調用了一次AddRef()。但智能指針析構時隻有一次Release()調用,是以,這個時候COM的引用技術沒有歸零,進而不會析構。但函數結束後,沒有任何指針能夠再次通路到COM元件。是以它在記憶體中“遊離”了,資源洩漏也就産生了。

問題的關鍵在于接口指針與智能指針混合使用了。而在最後忘記調用接口指針的Release()進而導緻了資源洩漏。

可能你會在此函數最末尾補上一句“pCalculator->Release()”,但非常不推薦你這樣做。因為我們引入智能指針的目的就是為了盡可能簡化我們的開發。若是将智能指針與接口指針混合使用,則會要話費更多的時間來思考引用計數問題。

是以最好的解決辦法是将接口指針從函數中完全剔除出來。上述函數完全可以由一下這個例子來完成:

void func(void)
{
    CComPtr<ICalculator>  pCalculator = NULL; //隻有智能指針
    hrRetCode = pCalculator .CoCreateInstance(CLSID_CALCULATOR,);//引用計數為1
    assert(hrRetCode);
    spCalculator->DoSomething();
}                            //調用Release()後引用計數為0,資源正确的被釋放了
           

混用智能指針會使得代碼引用計數難以琢磨,是以我們不應當在代碼中混用智能指針與接口指針。

但凡是都有例外,接口指針并非沒有用武之地。而且你已經在之前的章節中見到了這個問題。我們在函數參數傳遞過程中仍然使用了接口指針。

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

你可能還會有個疑惑,為什麼SomeApp( IHello * pHello )這個函數中的參數pHello不使用智能指針呢?

将智能指針放置到函數參數中這種做法并非不行,而是出于以下考慮我們選擇了接口指針作為參數傳遞的類型:

1.如果使用了智能指針,那麼此接口将無法在IDL檔案中描述。

2.使用智能指針作為參數傳遞會增加接口的複雜度,造成了解上的困難。

3.使用如果使用第三方的智能指針作為參數的類型,則釋出接口的時候需要将此智能指針的實作連同接口一起釋出出去。若使用者得不到此智能指針的實作,則無法使用此接口。

4.智能指針在傳遞過程中會産生更大的開銷,進而降低程式的性能。

……

若智能指針出現在函數傳回值之中,則危害會更大。試想一下如下代碼,将會産生何種後果?

CComPtr<IView> GetView(int nIndex)
{
    CComPtr<IView> spView = NULL;
    spView .CreateInstance(CLSID_MYCOMPONENT);
    return spView ;
}
           

但外面的調用過程如果是這樣呢?

IView *pIView  = NULL;
pIView = GetView();
pIView->DoSomething();
           

我們分析一下過程,GetView()傳回了一個臨時對象,這個對象是個智能指針,此時他的引用計數為1。當它将此智能指針指派給另外一個接口指針之後它便析構了。此時引用計數歸0,是以COM元件被釋放掉。之後随着pIview調用DoSomething()程式崩潰了~

現在你應該可以肯定這一條款:混用智能指針和接口指針會使得引用計數難以琢磨。但函數參數傳遞和函數傳回值中,我們允許接口指針的存在。