zoukankan      html  css  js  c++  java
  • COM编程之四 引用计数

    【1】客户为什么不应直接控制组件的生命期?

    假设一个组件A正在使用另一个组件B,可想组件A(客户)代码中肯定有若干个指向组件B接口的指针。

    那么这种情况下,当使用完一个接口而仍然在使用另一个接口时,是不能将组件释放掉的。

    而且很难知道两个接口指针是否指向同一组件,因此决定何时可以安全的释放一个组件将是极为困难的。

    得知两个接口指针是否是指向同一对象的唯一方法是查询这两个接口的IUnknown接口指针,然后对两者结果进行比较。

    当程序越来越复杂时,决定何时可以释放一个组件是极为复杂的事情。

    解决这个技术问题的办法:我们可以通知组件何时需要使用它的某个接口以及何时使用完接口,而不是直接将接口删除。

    对组件的释放也应该由组件在客户使用完其各个接口之后自己完成。

    IUnknown的两个成员函数AddRef和Release的作用就是给客户提供一种让它指示何时处理完一个接口的手段。

    【2】引用计数简介

    AddRef和Release实现的是一种名为引用计数的内存管理计数。

    引用计数是使组件能够自己将自己删除的最简单同时也是效率最高的方法。

    COM组件将维护一个称作是引用计数的数值。

    当客户从组件取得一个接口时,此引用计数将增1。

    当客户使用完某个接口后,组件的引用计数将减1。

    当引用计数值为0时,组件即可将自己从内存中删除。

    当创建某个已有接口的另外一个引用时,客户也将会增大相应组件的引用计数值。

    【3】正确地使用引用计数,遵循的规则

    (1)在返回之前调用AddRef。

    对于那些返回接口指针的函数,在返回之前应用相应的指针调用AddRef。

    这些函数包括QueryInterface及CreateInstance。

    这样当客户从这种函数得到一个接口后,它将无需调用AddRef。

    (2)使用完接口调用Release。

    在使用完某个接口之后应调用此接口的Release函数。

    以上两条代码如下:

     1 //Create a new component
     2 IUnknown* pLUnknown = CreateInstance();
     3 //Get interface IX
     4 IX* pIX = NULL;
     5 HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
     6 if (SUCCEEDED(hr))
     7 {
     8     pIX->fx();  //Use Interface IX
     9     pIX->Release();
    10 }
    11 pLUnknown->Release();

    (3)在赋值之后调用AddRef。

    在将一个接口指针赋给另外一个接口指针时,应调用AddRef。

    换句话说,在建立接口的另外一个应用之后应增加相应组件的应用计数。

    代码如下:

     1 //Create a new component
     2 IUnknown* pLUnknown = CreateInstance();
     3 //Get interface IX
     4 IX* pIX = NULL;
     5 HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
     6 if (SUCCEEDED(hr))
     7 {
     8     pIX->fx();  //Use Interface IX
     9     IX* pIX2 = pIX; //Make a copy of pIX
    10 
    11     pIX2->AddRef();
    12     pIX2->fx();   //Do  something
    13     pIX2->Release();
    14 
    15     pIX->Release();
    16 }

    【4】引用计数的完整示例

    代码如下:

      1 //
      2 // RefCount.cpp
      3 // To compile, use: cl RefCount.cpp UUID.lib
      4 //
      5 #include <iostream>
      6 using namespace std;
      7 #include <objbase.h>
      8 
      9 void trace(const char* msg) { cout << msg << endl ;}
     10 
     11 // Forward references for GUIDs
     12 extern const IID IID_IX ;
     13 extern const IID IID_IY ;
     14 extern const IID IID_IZ ;
     15 
     16 // Interfaces
     17 interface IX : IUnknown
     18 {
     19     virtual void __stdcall Fx() = 0 ;
     20 } ;
     21 
     22 interface IY : IUnknown
     23 {
     24     virtual void __stdcall Fy() = 0 ;
     25 } ;
     26 
     27 interface IZ : IUnknown
     28 {
     29     virtual void __stdcall Fz() = 0 ;
     30 } ;
     31 
     32 
     33 //
     34 // Component
     35 //
     36 class CA : public IX, public IY
     37 {
     38     // IUnknown implementation
     39     virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;            
     40     virtual ULONG __stdcall AddRef() ;
     41     virtual ULONG __stdcall Release() ;
     42 
     43     // Interface IX implementation
     44     virtual void __stdcall Fx() { cout << "Fx" << endl ;}
     45 
     46     // Interface IY implementation
     47     virtual void __stdcall Fy() { cout << "Fy" << endl ;}
     48 
     49 public:
     50     // Constructor
     51     CA() : m_cRef(0) {}
     52 
     53     // Destructor
     54     ~CA() { trace("CA:     Destroy self.") ;}
     55 
     56 private:
     57     long m_cRef;
     58 } ;
     59 
     60 HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
     61 {     
     62     if (iid == IID_IUnknown)
     63     {
     64         trace("CA QI:  Return pointer to IUnknown.") ;
     65         *ppv = static_cast<IX*>(this) ;
     66     } 
     67     else if (iid == IID_IX)
     68     {
     69         trace("CA QI:  Return pointer to IX.") ;
     70         *ppv = static_cast<IX*>(this) ;
     71     }
     72     else if (iid == IID_IY)
     73     {
     74         trace("CA QI:  Return pointer to IY.") ;
     75         *ppv = static_cast<IY*>(this) ;
     76     }
     77     else
     78     {         
     79         trace("CA QI:  Interface not supported.") ;
     80         *ppv = NULL ;
     81         return E_NOINTERFACE;
     82     }
     83     reinterpret_cast<IUnknown*>(*ppv)->AddRef() ; 
     84     return S_OK ;
     85 }
     86 
     87 ULONG __stdcall CA::AddRef()
     88 {
     89     cout << "CA:     AddRef = " << m_cRef+1 << '.' << endl ;
     90     return InterlockedIncrement(&m_cRef) ;
     91 }
     92 
     93 ULONG __stdcall CA::Release() 
     94 {
     95     cout << "CA:     Release = " << m_cRef-1 << '.' << endl ;
     96 
     97     if (InterlockedDecrement(&m_cRef) == 0)
     98     {
     99         delete this ;
    100         return 0 ;
    101     }
    102     return m_cRef ;
    103 }
    104 
    105 //
    106 // Creation function
    107 //
    108 IUnknown* CreateInstance()
    109 {
    110     IUnknown* pI = static_cast<IX*>(new CA) ;
    111     pI->AddRef() ;
    112     return pI ;
    113 }
    114 
    115 //
    116 // IIDs
    117 //
    118 // {32bb8320-b41b-11cf-a6bb-0080c7b2d682}
    119 static const IID IID_IX = 
    120 {0x32bb8320, 0xb41b, 0x11cf,
    121 {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
    122 
    123 // {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
    124 static const IID IID_IY = 
    125 {0x32bb8321, 0xb41b, 0x11cf,
    126 {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
    127 
    128 // {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
    129 static const IID IID_IZ = 
    130 {0x32bb8322, 0xb41b, 0x11cf,
    131 {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
    132 
    133 //
    134 // Client
    135 //
    136 int main()
    137 {
    138     HRESULT hr ;
    139 
    140     trace("Client: Get an IUnknown pointer.") ;
    141     IUnknown* pIUnknown = CreateInstance() ;
    142 
    143 
    144     trace("Client: Get interface IX.") ;
    145 
    146     IX* pIX = NULL ; 
    147     hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX) ;
    148 
    149     if (SUCCEEDED(hr))
    150     {
    151         trace("Client: Succeeded getting IX.") ;
    152         pIX->Fx() ;          // Use interface IX.
    153         pIX->Release() ;
    154     }
    155 
    156 
    157     trace("Client: Get interface IY.") ;
    158 
    159     IY* pIY = NULL ;
    160     hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY) ;
    161     if (SUCCEEDED(hr))
    162     {
    163         trace("Client: Succeeded getting IY.") ;
    164         pIY->Fy() ;          // Use interface IY.
    165         pIY->Release() ;
    166     }
    167 
    168 
    169     trace("Client: Ask for an unsupported interface.") ;
    170 
    171     IZ* pIZ = NULL ;
    172     hr = pIUnknown->QueryInterface(IID_IZ, (void**)&pIZ) ;
    173     if (SUCCEEDED(hr))
    174     {
    175         trace("Client: Succeeded in getting interface IZ.") ;
    176         pIZ->Fz() ;
    177         pIZ->Release() ;
    178     }
    179     else
    180     {
    181         trace("Client: Could not get interface IZ.") ;
    182     }
    183 
    184 
    185     trace("Client: Release IUnknown interface.") ;
    186     pIUnknown->Release() ;
    187 
    188     return 0;
    189 }
    190 //Output
    191 /*
    192 Client: Get an IUnknown pointer.
    193 CA:     AddRef = 1.
    194 Client: Get interface IX.
    195 CA QI:  Return pointer to IX.
    196 CA:     AddRef = 2.
    197 Client: Succeeded getting IX.
    198 Fx
    199 CA:     Release = 1.
    200 Client: Get interface IY.
    201 CA QI:  Return pointer to IY.
    202 CA:     AddRef = 2.
    203 Client: Succeeded getting IY.
    204 Fy
    205 CA:     Release = 1.
    206 Client: Ask for an unsupported interface.
    207 CA QI:  Interface not supported.
    208 Client: Could not get interface IZ.
    209 Client: Release IUnknown interface.
    210 CA:     Release = 0.
    211 CA:     Destroy self.
    212  */

    【5】引用计数的优化

    正确使用引用计数规则(3)的示例代码做优化如下:

     1 //Create a new component
     2 IUnknown* pLUnknown = CreateInstance();
     3 //Get interface IX
     4 IX* pIX = NULL;
     5 HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
     6 if (SUCCEEDED(hr))
     7 {
     8     pIX->fx();  //Use Interface IX
     9     IX* pIX2 = pIX; //Make a copy of pIX
    10 
    11     //    pIX2->AddRef();   //unnecessary!!!
    12     pIX2->fx();   //Do  something
    13     //  pIX2->Release();    //unnecessary!!!
    14 
    15     pIX->Release();
    16 }

    关键是找出那些生命期嵌套在引用同一接口指针生命期内的接口指针。

     1 void Fun(IX* pIx)
     2 {
     3     pIx->Fx();
     4 }
     5 
     6 void main()
     7 {
     8     IUnknown* pLUnknown = CreateInstance();
     9     //Get interface IX
    10     IX* pIX = NULL;
    11     HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
    12     if (SUCCEEDED(hr))
    13     {
    14         Fun(pIX);
    15         pIX->Release();
    16     }
    17 }

    很显然,Fun的生命期包含在pIX的生命期中,因此对于传递给Fun的接口指针,无需调用AddRef和Release。

    在函数中,不必要对存在于局部变量的接口指针进行引用计数。

    因为局部变量的生命期同函数的生命期是一样的,因此也将包含在调用者的生命期内。

    但当从某个全部变量或向某个全局变量复制一个指针时,则需要对此进行引用计数。

    因为全局变量可以从任意函数中的任意地方被释放。

    【6】引用计数规则

    (1)输出参数规则

    输出参数指的是给函数的调用者传回一个值的函数参数。比如QueryInterface函数

    (2)输入参数规则。比如上面引用计数优化的例子。

    (3)输入输出参数规则

    对于输入输出参数传递进来的接口指针,必须在给它赋另外一个接口指针之前调用其Release。

    在函数返回之前,还必须对输出参数中所保存的接口指针调用AddRef。示例代码如下:

    1 void ExchangeForCachedPtr(int i, IX**ppIX)
    2 {
    3     (*ppIX)->Fx();
    4     (*ppIX)->Release();
    5     *ppIX = g_cache[i];
    6     (*ppIX)->AddRef();
    7     (*ppIX)->Fx();
    8 }

    (4)局部变量规则

    (5)全局变量规则

    (6)不能确定时的规则

    对于任何不能确定的情形,都应调用AddRef 和 Release对。

     

    总而言之,通过引用计数,客户可以控制接口的生命期,而组件本身可以决定何时将它从内存中删除。

     

    Good  Good Study, Day Day Up.

    顺序  选择  循环  总结

  • 相关阅读:
    ABP 往前端返回详细的错误信息
    ABP 报错1
    three.js 测试1
    three.js 添加 图形控制界面 gui
    three.js 设置透明度
    three.js 基础使用1
    three.js 添加环境光
    three.js 添加三维坐标系
    P2690 接苹果
    [USACO08FEB]修路Making the Grade
  • 原文地址:https://www.cnblogs.com/Braveliu/p/3436206.html
Copyright © 2011-2022 走看看