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

【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.

顺序  选择  循环  总结

COM编程之四 引用计数的更多相关文章

  1. obj-c编程11:内存管理和ARC(自动引用计数)

    乖乖隆地洞,这篇文章内容可是不得了,内存管理哦!首先,这个要是搞不明白,你就等着进程莫名其妙的挂死,或是疯狂申请内存却不释放,结果被OS杀死,不管是"自杀"还是"他杀&q ...

  2. iOS开发--引用计数与ARC

    以下是关于内存管理的学习笔记:引用计数与ARC. iOS5以前自动引用计数(ARC)是在MacOS X 10.7与iOS 5中引入一项新技术,用于代替之前的手工引用计数MRC(Manual Refer ...

  3. Objective-C内存管理之-引用计数

    本文会继续深入学习OC内存管理,内容主要参考iOS高级编程,Objective-C基础教程,疯狂iOS讲义,是我学习内存管理的笔记 内存管理 1 内存管理的基本概念 1.1 Objective-C中的 ...

  4. 第3月第2天 find symbolicatecrash 生产者-消费者 ice 引用计数

    1.linux find export find /Applications/Xcode.app/ -name symbolicatecrash -type f export DEVELOPER_DI ...

  5. ATL是如何实现线程安全的引用计数和多线程控制的

    ATL是如何实现线程安全的引用计数和多线程控制的 正如标题所示,这是我经常被问到的一个问题,而每次我都从头开始给人说一次,其实说来过程理解起来的确有点复杂. 我们的每一个ATL Server Obje ...

  6. 引用计数 vs. GC

    内存管理问题 内存管理是编程过程中的一个经典问题,早期在 C 语言时代,几乎都靠 malloc/free 手动管理内存.随着各个平台的发展,到现在被广泛采用的主要有两个方法: 引用计数 (ARC,Au ...

  7. ARC————自动引用计数

    一.内存管理/引用计数 1.引用计数式内存管理的方式(下面四种) 对象操作 OC方法 生成并持有对象 alloc/new/copy/mutableCopyd等方法 持有对象 retain方法 释放对象 ...

  8. [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到)

    原文:[推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) [推荐]ORACLE PL/SQL编程之四: 把游标说透(不怕做不到,只怕想不到) 继上两篇:ORACLE PL ...

  9. (转)C++——std::string类的引用计数

    1.概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功课,于是你把自己关在房间里 ...

随机推荐

  1. Java学习-030-JSON 之四 -- 判断 JSONObject 是否包含键值对

    前文对获取 JSON 数据封装方法,使之可通过类似于 cssSelector 的方法获取 JSON 数据,使获取数据变得简单.敬请参阅:模仿 cssSelector 封装读取 JSON 数据方法. 在 ...

  2. ubuntu mysql 远程连接

    最近需要远程连接mysql服务器,先进行简单的测试,过程记录于此. 参考链接: http://blog.chinaunix.net/uid-28458801-id-3445261.html http: ...

  3. Spring AOP 实现原理与 CGLIB 应用

    https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/ AOP(Aspect Orient Programming),也就是面向 ...

  4. 添加 Gradle 依赖与 build.gradle 配置初识

    添加 Gradle 我们可以到我们添加 Maven 依赖的网站 Maven Repository: Search/Browse/Explore http://mvnrepository.com/ 上查 ...

  5. jQuery学习之jQuery Ajax用法详解(转)

    [导读] jQuery Ajax在web应用开发中很常用,它主要包括有ajax,get,post,load,getscript等等这几种常用无刷新操作方法,下面我来给各位同学介绍介绍.我们先从最简单的 ...

  6. 2、JavaScript常用互动方法

    一.输出内容(document.write) document.write() 可用于直接向 HTML 输出流写内容.简单的说就是直接在网页中输出内容. 第一种:输出内容用“”括起,直接输出" ...

  7. 注册页面的简单搭建(H5)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. 运行sql server profiler所需的权限

    ********运行Sql Server Profiler所需的权限(performance)*********/ --EG. -- 使用TRACE帐户(Performancetest)跟踪Sql S ...

  9. Hadoop2.2.0 第一步完成MapReduce wordcount计算文本数量

    1.完成Hadoop2.2.0单机版环境搭建之后需要利用一个例子程序来检验hadoop2 的mapreduce的功能 //启动hdfs和yarn sbin/start-dfs.sh sbin/star ...

  10. PAT 解题报告 1004. Counting Leaves (30)

    1004. Counting Leaves (30) A family hierarchy is usually presented by a pedigree tree. Your job is t ...