AddRef和函数签名

时间:2009-02-11 10:40:24

标签: c++ coding-style refcounting

我总是使用以下规则来签署函数的签名,这些函数根据是否执行AddRef返回引用计数的对象,但是也想向同事解释...所以我的问题是,是下面描述的规则被广泛遵循的规则?我正在寻找(例如)提倡这种风格的编码规则的指针。


如果函数没有添加对象的引用,它应该作为函数的返回值返回:

class MyClass
{
protected:
    IUnknown *getObj() { return m_obj; }
private:
    IUnknown *m_obj;
};

但是,如果函数添加对象的引用,则对象的指针指针作为参数传递给函数:

class MyClass
{
public:
    void getObj(IUnknown **outObj) { *outObj = m_obj; (*outObj)->AddRef(); }
private:
    IUnknown *m_obj;
};

3 个答案:

答案 0 :(得分:2)

在创建新对象并且调用者必须拥有它的所有权时,使用引用计数智能指针更为典型。

答案 1 :(得分:2)

我在具有大量COM的项目中使用了相同的样式。当他们在NuMega工作时,一些叫做SoftICE的小东西向我学习了它。我认为这也是Don Box(here it is at Amazon)“Essential COM”一书中所教授的风格。在某个时间点,这本书被认为是COM的圣经。我认为,现在还不是唯一的原因是COM已经变得不仅仅是COM了。

所有这一切,我更喜欢CComPtr和其他智能指针。

答案 2 :(得分:2)

一种方法是永远不要使用函数的返回值。仅使用输出参数,如第二种情况。无论如何,在已发布的COM接口中,这已经是一个规则。

这是一个“官方”参考,但是,通常情况下,它甚至没有提到您的第一个案例:http://support.microsoft.com/kb/104138

但是在组件内部,禁止返回值会导致代码变得丑陋。具有可组合性更好 - 即将函数放在一起方便,将一个函数的返回值直接作为参数传递给另一个函数。

智能指针允许您这样做。它们在公共COM接口中被禁止,但非HRESULT返回值也是如此。因此,你的问题就消失了。如果要使用返回值传回接口指针,请通过智能指针进行。并将成员存储在智能指针中。

但是,假设由于某种原因你不想使用智能指针(顺便说一句,你疯了!)然后我可以告诉你,你的推理是正确的。您的函数充当“属性获取者”,在您的第一个示例中,它不应该AddRef

所以你的规则是正确的(尽管你的实现中存在一个错误,我会在一秒钟之内找到它,因为你可能没有发现它。)

此函数需要一个对象:

void Foo(IUnknown *obj);

它根本不需要影响obj的引用计数,除非它想将它存储在成员变量中。肯定 NOT 是Foo在Release之前调用obj之前的责任!想象一下会造成的混乱。

现在这个函数返回一个对象:

IUnknown *Bar();

我们经常喜欢编写函数,将一个函数的输出直接传递给另一个函数:

Foo(Bar());

如果Bar增加了返回的引用的引用计数,那么这将不起作用。谁会去Release呢?因此Bar不会调用AddRef。这意味着它正在返回它存储和管理的东西,即它实际上是一个属性获取者。

此外,如果主叫方正在使用智能指针,p

p = Bar();

任何理智的智能指针在分配对象时都会转到AddRef。如果BarAddRef - 好了,我们又会泄露一次。这实际上只是同一可组合性问题的一个特例。

输出参数(指向指针)是不同的,因为它们不会以相同的方式受到可组合性问题的影响:

同样,智能指针提供了最常见的情况,使用第二个示例:

myClass.getObj(&p);

智能指针不会在这里进行任何重新计数,因此getObj必须这样做。

现在我们来看看这个bug。假设智能指针p在将其传递给getObj ...

时已经指向某个东西

更正后的版本是:

void getObj(IUnknown **outObj) 
{
    if (*outObj != 0)
        (*outObj)->Release();

    *outObj = m_obj;
    (*outObj)->AddRef();  // might want to check for 0 here also
}

在实践中,人们经常犯这样的错误,以至于当我已经拥有一个对象时调用operator&时,我的智能指针会断言更简单。