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函数。
以上两条代码如下:
//Create a new component
IUnknown* pLUnknown = CreateInstance();
//Get interface IX
IX* pIX = NULL;
HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
if (SUCCEEDED(hr))
{
pIX->fx(); //Use Interface IX
pIX->Release();
}
pLUnknown->Release();
(3)在赋值之后调用AddRef。
在将一个接口指针赋给另外一个接口指针时,应调用AddRef。
换句话说,在建立接口的另外一个应用之后应增加相应组件的应用计数。
代码如下:
//Create a new component
IUnknown* pLUnknown = CreateInstance();
//Get interface IX
IX* pIX = NULL;
HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
if (SUCCEEDED(hr))
{
pIX->fx(); //Use Interface IX
IX* pIX2 = pIX; //Make a copy of pIX pIX2->AddRef();
pIX2->fx(); //Do something
pIX2->Release(); pIX->Release();
}
【4】引用计数的完整示例
代码如下:
//
// RefCount.cpp
// To compile, use: cl RefCount.cpp UUID.lib
//
#include <iostream>
using namespace std;
#include <objbase.h> void trace(const char* msg) { cout << msg << endl ;} // Forward references for GUIDs
extern const IID IID_IX ;
extern const IID IID_IY ;
extern const IID IID_IZ ; // Interfaces
interface IX : IUnknown
{
virtual void __stdcall Fx() = ;
} ; interface IY : IUnknown
{
virtual void __stdcall Fy() = ;
} ; interface IZ : IUnknown
{
virtual void __stdcall Fz() = ;
} ; //
// Component
//
class CA : public IX, public IY
{
// IUnknown implementation
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
virtual ULONG __stdcall AddRef() ;
virtual ULONG __stdcall Release() ; // Interface IX implementation
virtual void __stdcall Fx() { cout << "Fx" << endl ;} // Interface IY implementation
virtual void __stdcall Fy() { cout << "Fy" << endl ;} public:
// Constructor
CA() : m_cRef() {} // Destructor
~CA() { trace("CA: Destroy self.") ;} private:
long m_cRef;
} ; HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
{
if (iid == IID_IUnknown)
{
trace("CA QI: Return pointer to IUnknown.") ;
*ppv = static_cast<IX*>(this) ;
}
else if (iid == IID_IX)
{
trace("CA QI: Return pointer to IX.") ;
*ppv = static_cast<IX*>(this) ;
}
else if (iid == IID_IY)
{
trace("CA QI: Return pointer to IY.") ;
*ppv = static_cast<IY*>(this) ;
}
else
{
trace("CA QI: Interface not supported.") ;
*ppv = NULL ;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
return S_OK ;
} ULONG __stdcall CA::AddRef()
{
cout << "CA: AddRef = " << m_cRef+ << '.' << endl ;
return InterlockedIncrement(&m_cRef) ;
} ULONG __stdcall CA::Release()
{
cout << "CA: Release = " << m_cRef- << '.' << endl ; if (InterlockedDecrement(&m_cRef) == )
{
delete this ;
return ;
}
return m_cRef ;
} //
// Creation function
//
IUnknown* CreateInstance()
{
IUnknown* pI = static_cast<IX*>(new CA) ;
pI->AddRef() ;
return pI ;
} //
// IIDs
//
// {32bb8320-b41b-11cf-a6bb-0080c7b2d682}
static const IID IID_IX =
{0x32bb8320, 0xb41b, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; // {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
static const IID IID_IY =
{0x32bb8321, 0xb41b, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; // {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
static const IID IID_IZ =
{0x32bb8322, 0xb41b, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; //
// Client
//
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 ;
}
//Output
/*
Client: Get an IUnknown pointer.
CA: AddRef = 1.
Client: Get interface IX.
CA QI: Return pointer to IX.
CA: AddRef = 2.
Client: Succeeded getting IX.
Fx
CA: Release = 1.
Client: Get interface IY.
CA QI: Return pointer to IY.
CA: AddRef = 2.
Client: Succeeded getting IY.
Fy
CA: Release = 1.
Client: Ask for an unsupported interface.
CA QI: Interface not supported.
Client: Could not get interface IZ.
Client: Release IUnknown interface.
CA: Release = 0.
CA: Destroy self.
*/
【5】引用计数的优化
正确使用引用计数规则(3)的示例代码做优化如下:
//Create a new component
IUnknown* pLUnknown = CreateInstance();
//Get interface IX
IX* pIX = NULL;
HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
if (SUCCEEDED(hr))
{
pIX->fx(); //Use Interface IX
IX* pIX2 = pIX; //Make a copy of pIX // pIX2->AddRef(); //unnecessary!!!
pIX2->fx(); //Do something
// pIX2->Release(); //unnecessary!!! pIX->Release();
}
关键是找出那些生命期嵌套在引用同一接口指针生命期内的接口指针。
void Fun(IX* pIx)
{
pIx->Fx();
} void main()
{
IUnknown* pLUnknown = CreateInstance();
//Get interface IX
IX* pIX = NULL;
HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
if (SUCCEEDED(hr))
{
Fun(pIX);
pIX->Release();
}
}
很显然,Fun的生命期包含在pIX的生命期中,因此对于传递给Fun的接口指针,无需调用AddRef和Release。
在函数中,不必要对存在于局部变量的接口指针进行引用计数。
因为局部变量的生命期同函数的生命期是一样的,因此也将包含在调用者的生命期内。
但当从某个全部变量或向某个全局变量复制一个指针时,则需要对此进行引用计数。
因为全局变量可以从任意函数中的任意地方被释放。
【6】引用计数规则
(1)输出参数规则
输出参数指的是给函数的调用者传回一个值的函数参数。比如QueryInterface函数
(2)输入参数规则。比如上面引用计数优化的例子。
(3)输入输出参数规则
对于输入输出参数传递进来的接口指针,必须在给它赋另外一个接口指针之前调用其Release。
在函数返回之前,还必须对输出参数中所保存的接口指针调用AddRef。示例代码如下:
void ExchangeForCachedPtr(int i, IX**ppIX)
{
(*ppIX)->Fx();
(*ppIX)->Release();
*ppIX = g_cache[i];
(*ppIX)->AddRef();
(*ppIX)->Fx();
}
(4)局部变量规则
(5)全局变量规则
(6)不能确定时的规则
对于任何不能确定的情形,都应调用AddRef 和 Release对。
总而言之,通过引用计数,客户可以控制接口的生命期,而组件本身可以决定何时将它从内存中删除。
Good Good Study, Day Day Up.
顺序 选择 循环 总结
COM编程之四 引用计数的更多相关文章
- obj-c编程11:内存管理和ARC(自动引用计数)
乖乖隆地洞,这篇文章内容可是不得了,内存管理哦!首先,这个要是搞不明白,你就等着进程莫名其妙的挂死,或是疯狂申请内存却不释放,结果被OS杀死,不管是"自杀"还是"他杀&q ...
- iOS开发--引用计数与ARC
以下是关于内存管理的学习笔记:引用计数与ARC. iOS5以前自动引用计数(ARC)是在MacOS X 10.7与iOS 5中引入一项新技术,用于代替之前的手工引用计数MRC(Manual Refer ...
- Objective-C内存管理之-引用计数
本文会继续深入学习OC内存管理,内容主要参考iOS高级编程,Objective-C基础教程,疯狂iOS讲义,是我学习内存管理的笔记 内存管理 1 内存管理的基本概念 1.1 Objective-C中的 ...
- 第3月第2天 find symbolicatecrash 生产者-消费者 ice 引用计数
1.linux find export find /Applications/Xcode.app/ -name symbolicatecrash -type f export DEVELOPER_DI ...
- ATL是如何实现线程安全的引用计数和多线程控制的
ATL是如何实现线程安全的引用计数和多线程控制的 正如标题所示,这是我经常被问到的一个问题,而每次我都从头开始给人说一次,其实说来过程理解起来的确有点复杂. 我们的每一个ATL Server Obje ...
- 引用计数 vs. GC
内存管理问题 内存管理是编程过程中的一个经典问题,早期在 C 语言时代,几乎都靠 malloc/free 手动管理内存.随着各个平台的发展,到现在被广泛采用的主要有两个方法: 引用计数 (ARC,Au ...
- ARC————自动引用计数
一.内存管理/引用计数 1.引用计数式内存管理的方式(下面四种) 对象操作 OC方法 生成并持有对象 alloc/new/copy/mutableCopyd等方法 持有对象 retain方法 释放对象 ...
- [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到)
原文:[推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) [推荐]ORACLE PL/SQL编程之四: 把游标说透(不怕做不到,只怕想不到) 继上两篇:ORACLE PL ...
- (转)C++——std::string类的引用计数
1.概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功课,于是你把自己关在房间里 ...
随机推荐
- (copy) Linux Commands Cheat Sheet in Black & White
source: http://linoxide.com/linux-command/linux-commands-cheat-sheet/
- SUSE Linux 防火墙设置
1.vim /etc/sysconfig/SuSEfirewall2 #编辑防火墙设置 FW_SERVICES_EXT_TCP="22 5901" #开启 ...
- iOS 自定义UIButton(图片和文字混合)
// UIApplicationDelegate .h文件 #import <UIKit/UIKit.h> @interface AppDelegate : UIResponder &l ...
- SweetAlert2 使用教程
SweetAlert2是一款功能强大的纯Js模态消息对话框插件.SweetAlert2用于替代浏览器默认的弹出对话框,它提供各种参数和方法,支持嵌入图片,背景,HTML标签等,并提供5种内置的情景类, ...
- pyOpenSSL0.13安装失败
/usr/lib64/python2.4/distutils/dist.py:236: UserWarning: Unknown distribution option: 'zip_safe' war ...
- Radius session
1,EAP 中继 client start, NAS require identity, client sent username, NAS sent username to sever, serve ...
- 异步调用webservice
一.异步调用 asynchronous call(异步调用):一个可以无需等待被调用函数的返回值就让操作继续进行的方法 举例: 异步调用就是你 喊 你朋友吃饭 ,你朋友说知道了 ,待会忙完去找你 ,你 ...
- Lintcode: Singleton && Summary: Synchronization and OOD
Singleton is a most widely used design pattern. If a class has and only has one instance at every mo ...
- Codeforce Round #219 Div2
妈蛋,C题又没搞出来! 看上去很简单的一题 到是这次的题目意思都比较容易懂,C没弄出来时,回去看了下A,以为来不及了,没想到这次的手速还是可以的7分钟搞出来了,因为太简单- -! A:大于两倍的不行- ...
- css 固定表头的表格,和 width:auto, margin:auto等 自计算方法
实现思路: 外层用一个table,里面写好Header,然后里面再写一个table里面写好header.然后自己控制overflow的值使内部的tablemargin-top和外层的行高一致就可以实现 ...