1

我一直使用以下规则来签名函数的签名,这些函数根据它们是否执行 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;
};
4

3 回答 3

2

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

于 2009-02-11T10:59:34.537 回答
2

我在有很多 COM 的项目中使用了相同的样式。有几个人在 NuMega 开发一个叫做 SoftICE 的小东西时学会了它。我认为这也是 Don Box 所著的“Essential COM”一书中所教授的风格(这里是亚马逊)。这本书曾一度被认为是 COM 的圣经。我认为情况并非如此的唯一原因是 COM 已不仅仅是 COM。

尽管如此,我更喜欢 CComPtr 和其他智能指针。

于 2009-02-11T11:40:11.833 回答
2

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

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

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

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

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

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

这个函数需要一个对象:

void Foo(IUnknown *obj);

它根本不需要影响obj的引用计数,除非它想将它存储在成员变量中。在它返回之前调用它当然不应该是 Foo 的责任!想象一下会造成的混乱。Releaseobj

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

IUnknown *Bar();

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

Foo(Bar());

Bar如果增加了它返回的任何内容的引用计数,这将不起作用。谁Release去呢?所以Bar不叫AddRef。这意味着它正在返回它存储和管理的东西,即它实际上是一个属性获取器。

此外,如果调用者使用智能指针,则p

p = Bar();

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

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

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

myClass.getObj(&p);

智能指针不会在这里进行任何引用计数,所以getObj必须这样做。

现在我们来看看这个错误。假设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&当它已经有一个对象时调用它更简单。

于 2009-02-11T11:42:27.397 回答