ATL智能指针类的两个小细节

  文章参考王志飞——《ATL智能指针类CComPtr与CComQIPtr解析》。

  首先根据我自己对CComPtr和CComQIPtr智能指针类是什么的理解:智能指针类是为了统一管理接口指针类的模板类。

  文章中有两个比较有嚼头的小细节:

    (1)CComPtr类的对象只能用与自己相同类型指针或者相同类型的CComPtr 类对象进行初始化, 而CComQIPtr可以用不同类型的指针或者类对象进行初始化,例如:

 1 IMyInterface3 v-pMyInter1=NULL;
 2 ……….
 3 PP指针v-pMyInter1指向实际COM对象
 4 PP下面是CComPtr类实例化语句
 5 CComPtr <IMyInterface>
 6 v-pMy1 = v-pMyInter1;
 7 PP指针类型是相同: IMyInterface, 正确
 8 CComPtr <IMyInterface>
 9 v-pMy2 = v-pMy1;
10 PP指针类型相同: IMyInterface, 正确
11 CComPtr <IYourInterface> v-pYour1 = v-pMy1;
12 PP指针类型不相同: IYourInterfacePPIMyInterface, 不正确
13 PP下面是CComQIPtr类实例化
14 CComQIPtr <IYourInterface> v-pYour2 = v-pMy1;
15 PP指针类型不相同: IYourInterfacePPIMyInterface, 也正确
View Code


原因是:在CComQIPtr 类对象v-pYour2初始化时, 尽管接口类型不一致, 可编译仍然无误的原因是, CComQIPtr
类先调用v-pMy1的QueryInterface方法查询IYourInterface接口, 如果找到相应接口对象, 则给指针成员变量
p赋值, 如果没有查找到相应接口对象, 则给成员变量赋值为NULL.也就是说把T类型的指针赋值为NULL。

    (2)智能指针类提供了释放所管理接口对象的方法Release () , 该方法既可以在必要时显示调用, 也可以在析构时自动被析构函数调用(就是说析构函数也会this.Release();)。该方法如下:

1 voidRelease () 
2 {
3     T3 pTemp = p;
4     If (pTemp)
5  {
6     p = NULL;
7     pTemp- >Release () ;
8   }
9 }
View Code

  先来看一下文章中的解释:原本简单的方法只需调用智能指针的成员变量p的Release方法即可, 而临时变量pTemp的出现, 则是因为这样写可以避免出现智能指针被保存的接口释放两次的事情发生. 例如, 智能指针m-pB是类A的成员变量, 这个指针保存了类B对象的一个引用, 而B的对象中又保存了一个类A的对象的引用, 当调用
了m-pB1Release () 方法, 试图释放B的对象时, B的对象先去释放对A对象的引用, A对象析构时调用智能指针类的析构函数, 这样对象B的Release方法就被调用了两次, 为避免两次释放对象B给内存带来的破坏, ATL310以后将代码修改成上述情形.同样值得注意的是, 我们在显示释放智能指针管理的对象时, 必须调用智能指针类的成员函数.Release () , 而不能调用智能指针类的成员p的- >Release () , 原因是1Release () 方法可以对成员变量p赋值为NULL, 防止在智能指针析构时再次调用- >Release () , 造成内存损坏。

  看完上边这段的描述,起初我是一头雾水,我认为由于深拷贝的原因,不可能存在两个对象里指针对象对彼此相互引用的问题。后来想了想,深拷贝只有在对整个对象进行赋值的时候才会发生,但在平时编程当中有的程序员可能会对一个对象内的一个指针变量直接赋值,比如A.p = &B;这样子就不会有深拷贝了,这样就会埋下文章中所说的隐患。现在明白为什么linux之父所说的了:c++是糟糕程序员的垃圾语言。这个疑问解决了,紧接着我又有一个疑问了:同一个对象的析构函数可以调用两次么?原来,作者所说的第一次没有调用A对象的析构函数,而是直接A.p->Release();B对象调用A的析构函数的时候是第一次。代码中的写法是在调用p的release();之前先判断是否已经释放过了,如果释放过了就不再释放了,防止产生内存问题。这是ATL作者的无奈之举吧,毕竟得考虑到我们这些“糟糕程序员”。呵呵。

posted on 2013-06-26 19:33  滴石之水  阅读(568)  评论(0编辑  收藏  举报

导航