如何更好地初始化不可创建的COM对象的引用计数器?

时间:2010-01-15 07:43:03

标签: c++ windows visual-c++ com reference-counting

我有一个带有返回对象的方法的COM接口:

interface ICreatorInterface {
    HRESULT CreateObject( IObjectToCreate** );
};

关键是调用ICreatorInterface::CreateObject()是检索实现IObjectToCreate接口的对象的唯一方法。

在C ++中,我可以这样做:

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 0
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();
     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result );
     if( FAILED(hr) ) {
         delete newObject;
     }
     return hr;
 }

或者这样

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 1
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();
     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result );
     // if QI() failed reference count is still 1 so this will delete the object
     newObject->Release();
     return hr;
 }

区别在于如何初始化引用计数器以及在QueryInterface()失败的情况下如何实现对象删除。由于我完全控制CCreatorInterfaceImplCObjectToCreateImpl,我可以选择其中任何一种方式。

IMO第一个变体更清晰 - 所有引用计数的东西都在一段代码中。我监督过什么吗?为什么第二种方法会更好?以上哪个更好,为什么?

3 个答案:

答案 0 :(得分:3)

这两种变体都违反了COM的基本原则

  • 永远不要在引用计数为零的COM对象上调用除AddRef之外的任何方法。

否则会导致各种错误。简单地说,因为它阻止人们对对象进行完全合法的操作。就像将它们放入智能指针一样。智能指针将调用AddRef,将计数置于1,稍后释放将计数置于0并使对象自毁。

是的,我意识到QueryInterface的90%的实现都没有这样做。但我也向你保证,那里有一些可以做到的:)

我认为最简单的方法是在创建对象后立即调用AddRef。这允许对象在尽可能早的时刻表现得像普通的COM对象。

我在过去遇到过这个问题,并且我编写了一个很好的小助手方法(假设该对象是在ATL中实现的)。

template <class T>
static 
HRESULT CreateWithRef(T** ppObject)
{
    CComObject<T> *pObject;
    HRESULT hr = CComObject<T>::CreateInstance(&pObject);
    if ( SUCCEEDED(hr) )
    {
        pObject->AddRef();
        *ppObject = pObject;
    }

    return hr; 
}

答案 1 :(得分:2)

Raymond Chen在他的博客上写了一篇相关文章: On objects with a reference count of zero

答案 2 :(得分:0)

我总是使用以下代码场景来创建返回的com对象,以避免内存问题。当然这是有效的,因为我的对象在创建时被引用count = 0。对于我而言,这总是比使用delete运算符来处理错误条件更清晰。

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 0
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();

     newObject->AddRef();

     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result);

     newObject->Release(); // release my addref, if QI succeeded it AddRef'd, if not the object is destroyed

     return hr; // return result from QI
 }