我总是使用以下规则来签署函数的签名,这些函数根据是否执行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;
};
答案 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
。如果Bar
也AddRef
- 好了,我们又会泄露一次。这实际上只是同一可组合性问题的一个特例。
输出参数(指向指针)是不同的,因为它们不会以相同的方式受到可组合性问题的影响:
同样,智能指针提供了最常见的情况,使用第二个示例:
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&
时,我的智能指针会断言更简单。