严以律己,宽以待人. 三思而后行. GMail/GTalk: yanglinbo#google.com; MSN/Email: tx7do#yahoo.com.cn; QQ: 3 0 3 3 9 6 9 2 0 .
使用引记数,就算是再历害的高手也难免会出错。而一但出错了,之后再去查问题可就相当的困难了。正如我曾经看到,有一段代码是这样的:
m_spView->Release();m_spView->Release();m_spView->Release();
看到这段代码,就知道引用计数出问题了。他想通过这种方式,把多出来的计数Release掉。但这么做能解决问题吗?答案是不能,这样的代码还可能造成严重的稳定性问题。解决引用计数问题,除了要了解引用计数规则外,我们还提昌要用智能指针。智能能帮助我们很好的处理引用计数问题。
在用VC开发应用程序时,有两个引用计数类可供我们使用。_com_ptr_t与CComPtr,它们都能很好的帮助我们解决引用计数处理。但这两个类还是有一点小小的区别,有的时候这一点区别也是致命的,因此我们必须清楚它们的差别。下面我罗列了它们之间的差别:
这些区别,导致了有些代码不能同时应用于两个智能指针。
以上代码会导致引用计数出错,前面的9个View的引用计数并没有Release。CComPtr<IView>的&运算符,会返回IView**也就是CComPtr内部成员的地址,但它不释放原来的指针。而GetView又会修改指针,直接把原来的指针抛弃了。
这个代码可以这样改:
把指针作为循环的局部变量,这样每次循环退出前spView都会被析构,最终调用Release。当然还能这样改:
_com_ptr_t的&运算符会帮助我们把原来的指针Release掉,所以我们就不必担心引用计数没有释放。
然我们使用的智能指针,就不要再去调AddRef或Release了。如果再去手工调用它们,就失去了智能指针的好处。CComPtr有一个非常巧妙的方法,禁止调用这两个方法。它声明了一个类_NoAddRefReleaseOnCComPtr,它的定义如下:
我们看到,里面就定义了两个私有函数。AddRef与Release,它们重写了IUnknown的这两个方法,并且继承自模板T。再来看段代码:
我们看到的是CComPtr的”->”运算符,它将内部的指针强制转换成_NoAddRefReleaseOnCComPtr<T>*。其中T是CComPtr的模板参数,也就是接口指针类型。可以看出_NoAddRefReleaseOnCComPtr<T>继承自接口类型,因此通过_NoAddRefReleaseOnCComPtr<T>*可以调用T的所有函数。前面我们看到NoAddRefReleaseOnCComPtr的两个私用函数,AddRef与Release,如果有谁想调用就会报编译错误。
_com_ptr_t有多个=运算符版本,代码如下:
其中template<typename _InterfaceType> _com_ptr_t& operator=(_InterfaceType* p)是一个模板函数,接受任意类型的指针,函数内部会调用传入参数”p”的QueryInterface。
template<> _com_ptr_t& operator=(Interface* pInterface) throw()是模板函数的一个偏特化版本,接受_com_ptr_t模板参数中指定的指针类型。当传入的接口指针类型,与类模板指定的类型一样时这个函数会被调用。它不需要做QueryInterface的调用,只是简单的AddRef;
综上所述,两个智能指针在同一份代码里混用,很可能导致不良后果。所以我认为,最好不要在同一份代码里混用。而这两个指针,我很喜欢_com_ptr_t,它在许多方面明显优于CComPtr。
使用了智能指针,也并不是高枕无忧了。它还是给我们带来了一些新的问题。
有一些第三方类库设计的不合理,它在函数的返回值里返回接口指针。如下代码就导会导致引用计数泄漏:
以上代码,注意调用GetView的地方。IViewPtr是智能指针,它的=运算符是会再调用AddRef而GetView里已经调了一次AddRef了,这里多了一次AddRef。别问我GetView中为什么要AddRef,这是引用计数规则,不清楚请看《引用计数我不怕之引用计数规则》。
解决这个问题的方法就是用Attach函数
也许是有人写了Attach,而其它人不明白Attach的意思,结果写出了这样的代码。
根据引用计数规则,将指针保存为副本,必须AddRef。但是这个例子里没有这么干,结果m_spView变成了野指针。
前面我们看到的GetView很简单,但是下面我们要做一别的事情,于是要用智能指针。
表面看来没什么问题,但在函数返回后,智能指针又会调用一次Release。要解决这个问题,可以调用Detach。
Detach还是会被乱用,看到这样的代码还真是哭笑不得。
这段代码能导致两个问题
泄漏是由于Detach返回IView*,并不会Release,而spView又会再调用一次AddRef。智能指针的Detach是会把自己设成空的,否则还叫什么Detach。
使用智能指针,是解决引用计数问题最好的办法。不要因为用智能指针,会引入新的问题,而放弃使用它。只要花心思搞清楚智能指针的不同点,使用时注意一些细节问题,使用起来应该会变的非常轻松。
posted on 2011-01-18 16:14 杨粼波 阅读(1506) 评论(0) 编辑 收藏 引用
Powered by: C++博客 Copyright © 杨粼波