Mr.C64
Mr.C64

Reputation: 42984

Proper way of calling AddRef() inside QueryInterface() implementation

I found some implementation pattern of QueryInterface() along these lines:

// 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;
}

The line of interest is the one marked with the // ### comment.

Is calling AddRef() on the IUnknown static_cast-pointer really necessary? Or is it just useless boilerplate code?
In other words, would a simple AddRef() call (i.e. this->AddRef()) be just fine? If not, why?

Upvotes: 2

Views: 541

Answers (3)

acelent
acelent

Reputation: 8135

The main reasons are tear-off interface pointers (e.g. for rarely used interfaces) and aggregable objects (the COM equivalent of mixins, more or less).

In these cases (a tear-off, or an aggregator when asked for an aggregated IID), the ppv is not an interface pointer to the same ref-counted C++ object. As such, that code is necessary if you want to support these cases just as well.

By calling this->AddRef, perhaps you earn a bit of simplicity or type safety, but at the cost of not supporting interfaces not explicitly implemented by the same C++ object.


P.S.: Contrary to what most books and documentation say, in my opinion:

  1. Aggregation is more similar to using mixins than it is to inheritance or composition;
  2. Aggregation is actually a special case of (cached) tear-off interface pointers as opposed to a special case of composition.

Here's my line of thought:

  1. When you inherit, you usually have the chance to override (virtual) methods, which is not the case with aggregation due to direct method calls; when you use composition, you may have to wrap inbound objects as to not let the inner object leak its identity to a given object (e.g. the inner object might pass itself to some method of the inbound object), whereas aggregation also means sharing identity by having two sets of IUnknown's methods on aggregable objects, thus not having this particular problem at all;
  2. A tear-off has its own lifetime, whereas an aggregable object shares its lifetime with the outer object. Otherwise, either may be created only when needed, although aggregators usually create aggregable objects as soon as they themselves are created.

Upvotes: 2

Hans Passant
Hans Passant

Reputation: 942000

Sure, you normally have only one AddRef() implementation so it doesn't matter how you call it. Note how the way the code uses ppv was the possible inspiration, it is typeless (void**) so a cast is needed. Maybe a tear-off would make you do this differently.

Upvotes: 2

Richard Critten
Richard Critten

Reputation: 2145

The interface returned need not be a base class of the class implementing QueryInterface.

Upvotes: 0

Related Questions