有效的使用和设计COM智能指针——条款13
条款13:必须提前释放COM组件时,别妄想智能指针帮你完成
有了智能指针,或许你不会想到要自己手动释放或者增加引用计数了。那么请欣赏一下下面这个函数:
view plaincopy to clipboardprint?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();
}
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:以类型安全的方式创资源和查询接口”所讲述的内容。看看下面这个修改版会不会存在问题。
view plaincopy to clipboardprint?void InSomewhere(IHello *pHello)
{
CComPtr<IHello> spHello = pHello;
spHello->DoSomething();
spHello.CoCreateInstance( CLSID_HELLO );//这里可能出现资源泄漏
assert(spHello);
spHello->DoOtherthing();
}
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组件时,别妄想智能指针帮你完成”。
view plaincopy to clipboardprint?void InSomewhere(IHello *pHello)
{
CComPtr<IHello> spHello = pHello;
spHello->DoSomething();
spHello = NULL; //或者spHello.Release(); 提前释放资源
spHello.CoCreateInstance( CLSID_HELLO );
assert(spHello);
spHello->DoOtherthing();
}
void InSomewhere(IHello *pHello)
{
CComPtr<IHello> spHello = pHello;
spHello->DoSomething();
spHello = NULL; //或者spHello.Release(); 提前释放资源
spHello.CoCreateInstance( CLSID_HELLO );
assert(spHello);
spHello->DoOtherthing();
}
OK,问题解决了。
更有一些情况下我们不知觉的导致了内存泄漏,原因同样是由于你没有手动释放COM组件。假设我们设计了这么一个容器来存放智能指针如下:
view plaincopy to clipboardprint?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;
};
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的过程是如下这样的:
view plaincopy to clipboardprint?MyStack<CComPtr<ICalculator>> g_myStack(1000);
void func()&nbs
补充:软件开发 , C语言 ,