在QueryInterface()实现中调用AddRef()的正确方法

时间:2015-01-03 22:44:13

标签: c++ windows com

我在这些方面找到了QueryInterface()的一些实现模式:

// Inside some COM object implementation ...

virtual HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
    *ppv = /* Find interface ... */
    if (*ppv == nullptr)
        return E_NOINTERFACE;

    static_cast<IUnknown *>(*ppv)->AddRef();  // ###         
    return S_OK;
}

感兴趣的行是标有// ###评论的行。

AddRef() IUnknown - 指针上调用static_cast真的有必要吗?或者它只是无用的样板代码?
换句话说,一个简单的AddRef()电话(即this->AddRef())会不会很好?如果没有,为什么?

3 个答案:

答案 0 :(得分:2)

当然,您通常只有一个 AddRef()实现,因此与您调用它的方式并不重要。请注意代码使用ppv的方式是可能的灵感,它是无类型的(void **),因此需要强制转换。也许撕掉会让你以不同的方式做到这一点。

答案 1 :(得分:2)

主要原因是撕下接口指针(例如,很少使用的接口)和可聚合对象(COM等效于mixins,或多或少)。

在这些情况下(请求汇总的IID时撕掉或聚合器),ppv不是指向同一引用计数的C ++对象的接口指针。因此,如果您也想支持这些案例,那么该代码是必要的。

通过调用this->AddRef,您可能会获得一些简单性或类型安全性,但代价是不支持未由同一C ++对象明确实现的接口。


P.S。:与大多数书籍和文献所说的相反,在我看来:

  1. 聚合更像是使用mixins而不是继承或组合;
  2. 聚合实际上是(缓存)撕下接口指针的特例,而不是特殊的组合情况。
  3. 这是我的思路:

    1. 当你继承时,你通常有机会覆盖(虚拟)方法,由于直接方法调用,聚合不是这种情况;当你使用组合时,你可能必须包装入站对象,以免内部对象将其身份泄露给给定对象(例如,内部对象可能将自身传递给入站对象的某个方法),而聚合也意味着通过在可聚合对象上有两组IUnknown方法,因此根本没有这个特殊问题;
    2. 撕裂有自己的生命周期,而可聚合对象与外部对象共享其生命周期。否则,可能只在需要时创建其中任何一个,尽管聚合器通常在创建它们后立即创建可聚合对象。

答案 2 :(得分:0)

返回的接口不必是实现QueryInterface的类的基类。