设为首页 收藏本站
查看: 2160|回复: 0

[经验分享] 有效的使用和设计COM智能指针 ——条款13:必须提前释放COM组件时,别妄想智能指针帮你完成

[复制链接]

尚未签到

发表于 2015-11-24 07:52:08 | 显示全部楼层 |阅读模式
条款13:必须提前释放COM组件时,别妄想智能指针帮你完成

更多条款请前往原文出处:http://blog.csdn.net/liuchang5

  有了智能指针,或许你不会想到要自己手动释放或者增加引用计数了。那么请欣赏一下下面这个函数:

void InSomewhere(IHello *pHello)
{
CComPtr<IHello> spHello = pHello;
spHello->DoSomething();
HRESULT hr =CoCreateInstance(
CLSID_HELLO
NULL,
CLSCTX_INPROC_SERVER,
IID_IHELLO,
(void **)&spHello     //这里可能出现资源泄漏
);
assert(SUCCEED(hr));
spHello->DoOtherthing();
}
  你认真的核对了一下IID和指针的类型,发现没有问题。并且CoCreateInstance后面这个assert让你对他的行为信心十足。
  但是问题是spHello原来是有所指的。他会释放掉相应接口的引用计数吗?答案是未定义。
  对于spHello在非空情况下的取地址操作,在不同智能指针中定义不尽相同。对CComPtr来说,他会在DEBUG的时候出发一个断言,而在RELEASE版本就悄悄的让资源泄漏了。而对于_com_ptr_t而言,无论是在RELASE还是DEBUG版本中它都会偷偷的先释放掉原油资源,而不给外界任何关于这种危险操作的提示。
  知道了原因可能你不会太期望用取地址符这种危险的操作了。你可能想到了用前面“条款11:以类型安全的方式创资源和查询接口”所讲述的内容。看看下面这个修改版会不会存在问题。

void InSomewhere(IHello *pHello)
{
CComPtr<IHello> spHello = pHello;
spHello->DoSomething();
spHello.CoCreateInstance( CLSID_HELLO );//这里可能出现资源泄漏
assert(spHello);
spHello->DoOtherthing();
}
  问题还是发生了,因为CoCreateInstance仍然只是在DEBUG版本中spHello非空的情况下做了一个断言。而RELEASE中资源就泄漏了。而对于_com_ptr_t来说,仍然是悄悄的先帮你把资源给释放掉。
  因此最稳妥的应对策略是,“必须提前释放COM组件时,别妄想智能指针帮你完成”。

void InSomewhere(IHello *pHello)
{
CComPtr<IHello> spHello = pHello;
spHello->DoSomething();
spHello = NULL;             //或者spHello.Release(); 提前释放资源
spHello.CoCreateInstance( CLSID_HELLO );
assert(spHello);
spHello->DoOtherthing();
}
  OK,问题解决了。
  更有一些情况下我们不知觉的导致了内存泄漏,原因同样是由于你没有手动释放COM组件。假设我们设计了这么一个容器来存放智能指针如下:

template<typename T>
class MyStack
{
public:
MyStack(int nCapacity){
m_pArray = new T[nCapacity]();
m_nCapacity = nCapacity;
m_nTop= 0;
};
~MyStack(){
delete[] m_pArray;
};
void push(T Item){
assert(m_nTop < m_nCapacity);
m_pArray[m_nTop++] = Item;
}
T pop(){
assert(m_nTop > 0);
return m_pArray[--m_nTop];
}
MyStack(const MyStack<T>& otherArray)
{
//deep copy it
};
private:
T *m_pArray;
int m_nTop;
int m_nCapacity;
};



  而使用这个MyStack的过程是如下这样的:

MyStack<CComPtr<ICalculator>> g_myStack(1000);
void func()
{
for (int i=0; i<500; i++)
{
CComPtr<ICalculator> spCalculator = NULL;
spCalculator.CoCreateInstance(CLSID_CALCULATOR);
g_myStack.push(spCalculator);
}
...
for (int i=0; i<500; i++)
{
CComPtr<ICalculator> spCalculator = g_myStack.pop();
spCalculator->DoSomething();
}
}
  500CoCreateInstance()这个操作或许会让你觉得有点疯狂。但我仅仅是想让你知道内存泄漏是如何潜在的发生的。
  其原因是什么? 智能指针无法在这种情况下自动帮我们释放掉相应的资源,首先他在全局空间上。如果想等待~MyStack()这个析构函数被调用可能需要等到程序结束。而在pop后我们不应当继续在MyStack中保存智能指针对接口的引用。试想,若之后这个MyStack中的元素永远达不到500个。那么,MyStack后半部分所持有的资源永远无法被释放掉。
  因此必须手动释放COM组件时,别妄想智能指针帮你完成,稍微改写一下pop()函数,资源泄漏问题解决了。

template<typename T>
class MyStack
{
public:
....
T pop(){
assert(m_nTop > 0);
T tempArray = m_pArray[--m_nTop];
m_pArray  = NULL;        //或者用 m_pArray.Release();
return  tempArray;
}
private:
....
};


  谨记在心,智能指针只是辅助我们管理内存的手段。必须提前释放COM资源时,别妄想智能指针帮你完成。这一节可以结束了。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-142809-1-1.html 上篇帖子: Linux网络编程IPv4和IPv6的inet_addr、inet_aton、inet_pton函数 下篇帖子: 为什么要阅读源代码?如何有效的阅读源代码? 选一些比较优秀的开源产品作为源代码阅读对象?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表