我最近看到了关于智能指针的PowerPoint和他们的陷阱,这张幻灯片有这张幻灯片(几乎没有评论或解释:
在上下文中:特别是_com_ptr_t,用于处理AddRef / Release的COM接口的智能指针,由_COM_SMARTPTR_TYPEDEF
宏创建。*
<小时/> 的错:
IObjectPtr spObj;
for (int i(0); i<MAX; i++)
{
//passed as actual , no release of previous ptr value
spOtherObj->get_Obj(&spObj);
}
下一张幻灯片声称如果将spObj
放在循环范围内就可以了:
<小时/> 从右:
for (int i(0); i<MAX; i++)
{
IObjectPtr spObj;
//passed as actual , no release of previous ptr value
spOtherObj->get_Obj(&spObj);
}
我研究了这个,但仍然无法弄清楚他们在谈论什么 第二个解决的第一个问题是什么?
<小时/> 我猜测,在更全面的背景下,正确/错误的代码看起来像: 虽然我的假设可能是错的
_COM_SMARTPTR_TYPEDEF(ICalendar, __uuidof(ICalendar))
void get_Calendar(ICalendarPtr* pCalendar)
{
*pCalendar.CreateInstance(__uuidof(Calendar));
}
void WrongMethod(void)
{
ICalendarPtr spCalendar;
for (int i(0); i<MAX; i++)
{
//passed as actual , no release of previous ptr value
get_Calendar(&spCalendar);
}
}
答案 0 :(得分:7)
这很可能是指ATL::CComPtr
而不是_com_ptr_t
。
问题是CComPtr::operator&
返回包装指针的地址但不释放它,因此假设被包装的接口不是NULL,则在对象被声明出循环时泄漏对象。
实现确认了这一事实,这是直接从ATL标题复制的,包括注释:
//The assert on operator& usually indicates a bug. If this is really
//what is needed, however, take the address of the p member explicitly.
T** operator&() throw()
{
ATLASSERT(p==NULL);
return &p;
}
_com_ptr_t
解决了这个问题,一般情况下使用起来比较方便,因此应该首选适用。
答案 1 :(得分:6)
这些是ATL :: CComPtr智能指针(它们非常聪明,顺便说一句)。
该对象类型的operator &
返回其中的原始接口指针的地址。因此,第一个循环实际上并不比这样做更好:
IObject* pObj = NULL;
for (int i(0); i<MAX; i++)
{
spOtherObj->get_Obj(&pObj);
}
每次迭代,都不会释放先前迭代的接口。它只是丢失,泄露,底层coclass的引用计数将被人为地锁定。
通过将智能指针移动到循环的内部,您现在允许智能指针对象的析构函数清理所获取的每个接口, ->Release()
,在下一次迭代之前。扩展的代码实际上是这样的:
for (int i(0); i<MAX; i++)
{
IObject* pObj = NULL;
spOtherObj->get_Obj(&pObj);
if (pObj) pObj->Release();
}