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

26 篇文章 0 订阅
25 篇文章 1 订阅

条款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资源时,别妄想智能指针帮你完成。这一节可以结束了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值