代码改变世界

com学习笔记(3)基本的com接口-引用计数的实现

2009-07-05 23:12  Clingingboy  阅读(1351)  评论(0编辑  收藏  举报

   clr内存是托管的,但c++不是.我们用完组件还得回收。那么就有一个问题。我们需要知道使用中的组件可以释放了吗?必须想一种机制在适当的时候来释放资源,于是就有了引用计数的概念.IUnknown还有其他两个成员,我们没有讲过

即AddRef和Release方法.用这两个方法可以有效的管理组件的生命周期。

AddRef则计数加1

Release则计数减1,若等于0则释放资源

interface IUnknown
{
    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) =0;            
    virtual ULONG __stdcall AddRef() =0 ;
    virtual ULONG __stdcall Release() =0 ;
};

 

一.引用计数三大规则

 

1.在返回接口指针时调用AddRef.

IUnknown* CreateInstance()
{
    IUnknown* pI = static_cast<IX*>(new CA) ;
    pI->AddRef() ;
    return pI ;
}

2.在使用完接口时调用Release.用户很懒,总是想让系统自己释放资源,但只有用户自己才知道什么时候不需要使用了.还得用户自己来调

记得接口用完后调用Release

如下代码

int main()
{
    HRESULT hr ;

    trace("Client: Get an IUnknown pointer.") ;
    IUnknown* pIUnknown = CreateInstance() ;


    trace("Client: Get interface IX.") ;

    IX* pIX = NULL ; 
    hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX) ;

    if (SUCCEEDED(hr))
    {
        trace("Client: Succeeded getting IX.") ;
        pIX->Fx() ;          // Use interface IX.
        pIX->Release() ;
    }


    trace("Client: Get interface IY.") ;

    IY* pIY = NULL ;
    hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY) ;
    if (SUCCEEDED(hr))
    {
        trace("Client: Succeeded getting IY.") ;
        pIY->Fy() ;          // Use interface IY.
        pIY->Release() ;
    }


    trace("Client: Ask for an unsupported interface.") ;

    IZ* pIZ = NULL ;
    hr = pIUnknown->QueryInterface(IID_IZ, (void**)&pIZ) ;
    if (SUCCEEDED(hr))
    {
        trace("Client: Succeeded in getting interface IZ.") ;
        pIZ->Fz() ;
        pIZ->Release() ;
    }
    else
    {
        trace("Client: Could not get interface IZ.") ;
    }


    trace("Client: Release IUnknown interface.") ;
    pIUnknown->Release() ;

    return 0;
}

 

3.在赋值后调用AddRef.

pIX->Fx() ;          // Use interface IX.
IX* pIX2=pIX;
pIX2->AddRef();
pIX2->Fx();
pIX2->Release() ;
pIX->Release() ;

 

二.引用计数接口单一维护

 

上面我们可以看到,每个引用计数接口都是单一维护,很麻烦.

为何不在同一个组件统一维护呢?解释是为了方便后续调试和资源的释放.等接口多了就很难管理,万一是全局设计,就很难找出是哪个接口的问题了.(我感觉这完全是设计问题,但确实是个问题)

三.引用计数的实现

 

返回值供调试使用,其他地方意义不大。

ULONG __stdcall CA::AddRef()
{
    cout << "CA:     AddRef = " << m_cRef+1 << '.' << endl ;
    return InterlockedIncrement(&m_cRef) ;
}

ULONG __stdcall CA::Release() 
{
    cout << "CA:     Release = " << m_cRef-1 << '.' << endl ;

    if (InterlockedDecrement(&m_cRef) == 0)
    {
        delete this ;
        return 0 ;
    }
    return m_cRef ;
}

 

到此为止,用户的使用从直接用delete关键字变成用Release释放资源.

四.何时进行引用计数

 

1.若生命周期包含,可以省却引用计数

pIX->Fx() ;          // Use interface IX.
IX* pIX2=pIX;
pIX2->AddRef();
pIX2->Fx();
pIX2->Release() ;
pIX->Release() ;

 

2.生命周期不包含则无法省却

顺序换一下就大不一样了.

IX* pIX2=pIX;
 pIX->Fx() ;
 pIX2->AddRef();
 pIX->Release() ;
 pIX2->Fx();
 pIX2->Release() ;

 

五.引用计数规则

 

下次继续