这个智能指针的使用有什么问题?

3
我最近看到了一份关于智能指针及其陷阱的PowerPoint演示文稿,其中有这张幻灯片(几乎没有评论或解释):

上下文:具体来说,_com_ptr_t是用_COM_SMARTPTR_TYPEDEF宏创建的用于处理AddRef/Release的COM接口的智能指针。


错误:

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);
    }
}

1
它也没有帮助,因为不清楚 get_Obj 在做什么。 - Collin Dauphinee
指针总是很聪明的 ;) - lordkain
1
这段内容主要涉及使用 _com_ptr_t 智能指针来管理 COM 对象。 - abelenky
2个回答

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 可以解决这个问题,并且通常更方便易用,因此应在适用的情况下优先选择。


非常好的回答。如果你是正确的,难怪我想不出来。我一直在看 _com_ptr_t,它没有这个问题! - abelenky
其实,直到看到你的问题,我完全忘记了关于CComPtr的这个问题 :) - Zdeslav Vojkovic
我记不清CComPtr是否仍然支持它,但它曾经有一个调试模式用于检测对非空实例调用operator &的情况,如果触发了这种情况,它会抛出一个断言。不知道它现在是否还有这个功能。 - WhozCraig
是的,它仍然有检查,我刚在源代码中查看了一下 :) - Zdeslav Vojkovic

6
这是 ATL::CComPtr 智能指针(顺便说一下,它们并不十分智能)。
该对象类型的 & 运算符返回其中原始接口指针的地址。因此,第一个循环与执行以下操作没有太大区别:
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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接