zoukankan      html  css  js  c++  java
  • com学习笔记(3)基本的com接口引用计数的实现

       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() ;

    五.引用计数规则

    下次继续

  • 相关阅读:
    互斥量
    读写锁
    死锁
    pthread
    线程
    守护进程
    信号捕捉
    信号集
    信号
    mmap
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/1517362.html
Copyright © 2011-2022 走看看