有效的使用和设计COM智能指针——条款2:引用计数的是与非
条款2:引用计数的是与非
使用COM做开发的程序员往往会被接口引用计数所带来的问题搞得头破血流。引用计数这个老大难问题存在的原因也相当简单:在COM开发中,客户仅仅知道组件的接口。当使用完一个接口而要使用另外一个接口时,由于客户并不知道两个接口是否指向同一组件,因此客户无法直接将组件释放。COM组件的生命周期也因此无法被客户方便的控制。
于是为了解决这一问题,COM组建使用了引用计数。每个组件根据引用计数的数值决定何时释放自身所占用的资源。当客户从某个组件查询出一个接口时,此引用计数值将增1。当客户使用完此接口时,此引用值减1。如果某个组件的引用计数值降至0时候,组件则会将自己从内存中删除。
通过引用计数确实可以很合理的管理组件的生命周期,但也严格要求开发人员遵循下面这三条简单规则【1】:
1.在返回之前调用AddRef。对于那些返回接口指针的函数,在返回前应用相应的指针调用AddRef。这些函数包括QueryInte易做图ce及CreateInstance。这样当客户从这种函数得到一个接口后,他将无需调用AddRef。
2.使用完接口之后调用Release。在使用完某个接口之后应调用此接口的Release函数。
3.在赋值之后调用AddRef。将一个接口指针赋值给另外一个接口指针时,应调用AddRef。换句话说,在建立接口的另外一个引用之后应增加相应组件的引用计数。
根据如上三条原则我们需要编写出了形如下例的代码:
view plaincopy to clipboardprint?void SomeApp( IHello * pHello )
{
IHello* pCopy = pHello;
pCopy->AddRef();
OtherApp();
pCopy->Hello();
pCopy->Release();
}
void SomeApp( IHello * pHello )
{
IHello* pCopy = pHello;
pCopy->AddRef();
OtherApp();
pCopy->Hello();
pCopy->Release();
}
这三条规则看似简单,但是程序员若在某一时刻遗漏掉其中的某条规则,则会使得引用计数陷入混乱。出错后,最乐观的情况是程序由于访问了已经被释放的资源,而直接崩溃。如果运气不是特别好,那么资源泄露则悄无声息的发生了。
可以看出由于引用计数的引入,COM组件的生命周期可以自行管理,但同时也使得COM的使用变得非常危险。因为使用过程中需要每一个使用者都要严格并且正确的调用AddRef()和Release(),一旦出现问题,就会造成对象不能被正常释放,或者对象被重复删除,造成程序崩溃。所以使用COM接口,必须小心翼翼才行。
我们试着用智能指针改写上述代码。这里以CComPtr为例,更多关于它的介绍我们放到后续条款中。上述函数将会变成如下这种形式:
view plaincopy to clipboardprint?void SomeApp( IHello * pHello )
{
CComPtr<IHello> spHello= pHello;
OtherApp();
spHello->Hello();
}
void SomeApp( IHello * pHello )
{
CComPtr<IHello> spHello= pHello;
OtherApp();
spHello->Hello();
}
智能指针给我带来了引用计数的半自动化处理(之所一说是半自动化,是因为有些地方仍然需要手动管理引用计数)。AddRef()和Release()操作不见了,代码简洁了不少,而更重要的是它还将带来更多的便利条件。我们在之后的条款中会进一步讨论。
作者“liuchang5的专栏”
补充:软件开发 , C语言 ,