一种方法是从不使用函数的返回值。仅使用输出参数,如第二种情况。无论如何,这在已发布的 COM 接口中已经是一条规则。
这是一个“官方”参考,但通常情况下,它甚至没有提到你的第一个案例:http: //support.microsoft.com/kb/104138
但是在组件内部,禁止返回值会使代码变得丑陋。具有可组合性要好得多 - 即方便地将函数组合在一起,将一个函数的返回值直接作为参数传递给另一个函数。
智能指针允许您这样做。它们在公共 COM 接口中被禁止,但非 HRESULT 返回值也是如此。因此,您的问题就消失了。如果要使用返回值来传回接口指针,请通过智能指针进行。并将成员存储在智能指针中。
但是,假设由于某种原因您不想使用智能指针(顺便说一句,您疯了!)那么我可以告诉您您的推理是正确的。您的函数充当“属性获取器”,在您的第一个示例中它不应该AddRef
。
所以你的规则是正确的(尽管你的实现中有一个错误,我会在一秒钟内找到它,因为你可能没有发现它。)
这个函数需要一个对象:
void Foo(IUnknown *obj);
它根本不需要影响obj
的引用计数,除非它想将它存储在成员变量中。在它返回之前调用它当然不应该是 Foo 的责任!想象一下会造成的混乱。Release
obj
现在这个函数返回一个对象:
IUnknown *Bar();
我们经常喜欢组合函数,将一个函数的输出直接传递给另一个函数:
Foo(Bar());
Bar
如果增加了它返回的任何内容的引用计数,这将不起作用。谁Release
去呢?所以Bar
不叫AddRef
。这意味着它正在返回它存储和管理的东西,即它实际上是一个属性获取器。
此外,如果调用者使用智能指针,则p
:
p = Bar();
AddRef
当它被分配一个对象时,任何理智的智能指针都会指向它。如果Bar
还AddRef
-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&
当它已经有一个对象时调用它更简单。