Why exactly do I need an explicit upcast when implementing QueryInterface() in an object with multiple interfaces()

c++, com, multiple-inheritance, visual-c++

Solution

The problem is that `*ppv` is usually a `void*` - directly assigning `this` to it will simply take the existing `this` pointer and give `*ppv` the value of it (since all pointers can be cast to `void*`).

This is not a problem with single inheritance because with single inheritance the base pointer is always the same for all classes (because the vtable is just extended for the derived classes).

However - for multiple inheritance you actually end up with multiple base pointers, depending on which 'view' of the class you're talking about! The reason for this is that with multiple inheritance you can't just extend the vtable - you need multiple vtables depending on which branch you're talking about.

So you need to cast the `this` pointer to make sure that the compiler puts the correct base pointer (for the correct vtable) into `*ppv`.

Here's an example of single inheritance:

class A {
  virtual void fa0();
  virtual void fa1();
  int a0;
};

class B : public A {
  virtual void fb0();
  virtual void fb1();
  int b0;
};

vtable for A:

[0] fa0
[1] fa1

vtable for B:

[0] fa0
[1] fa1
[2] fb0
[3] fb1

Note that if you have the `B` vtable and you treat it like an `A` vtable it just works - the offsets for the members of `A` are exactly what you would expect.

Here's an example using multiple inheritance (using definitions of `A` and `B` from above) (note: just an example - implementations may vary):

class C {
  virtual void fc0();
  virtual void fc1();
  int c0;
};

class D : public B, public C {
  virtual void fd0();
  virtual void fd1();
  int d0;
};

vtable for C:

[0] fc0
[1] fc1

vtable for D:

@A:
[0] fa0
[1] fa1
[2] fb0
[3] fb1
[4] fd0
[5] fd1

@C:
[0] fc0
[1] fc1
[2] fd0
[3] fd1

And the actual memory layout for `D`:

[0] @A vtable
[1] a0
[2] b0
[3] @C vtable
[4] c0
[5] d0

Note that if you treat a `D` vtable as an `A` it will work (this is coincidence - you can't rely on it). However - if you treat a `D` vtable as a `C` when you call `c0` (which the compiler expects in slot 0 of the vtable) you'll suddenly be calling `a0`!

When you call `c0` on a `D` what the compiler does is it actually passes a fake `this` pointer which has a vtable which looks the way it should for a `C`.

So when you call a `C` function on `D` it needs to adjust the vtable to point to the middle of the `D` object (at the `@C` vtable) before calling the function.

Problem

Assume I have a class implementing two or more COM interfaces: ``` class CMyClass : public IInterface1, public IInterface2 { }; ``` Almost every document I saw suggests that when I implement QueryInterface() for IUnknown I explicitly upcast this pointer to one of the interfaces: ``` if( iid == __uuidof( IUnknown ) ) { *ppv = static_cast<IInterface1>( this ); //call Addref(), return S_OK } ``` The question is why can't I just copy this? ``` if( iid == __uuidof( IUnknown ) ) { *ppv = this; //call Addref(), return S_OK } ``` The documents usually say that if I do the latter I will violate the requirement that any call to QueryInterface() on the same object must return exactly the same value. I don't quite get that. Do they mean that if I QI() for IInterface2 and call QueryInterface() through that pointer C++ will pass this slightly different from if I QI() for IInterface2 because C++ will each time make this point to a subobject?

Original source