为什么我需要一个明确的upcast当实现QueryInterface()在一个具有多个接口的对象() [英] Why exactly do I need an explicit upcast when implementing QueryInterface() in an object with multiple interfaces()

查看:132
本文介绍了为什么我需要一个明确的upcast当实现QueryInterface()在一个具有多个接口的对象()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个类实现两个或更多的COM接口:

Assume I have a class implementing two or more COM interfaces:

class CMyClass : public IInterface1, public IInterface2 {
};

几乎我看到的每个文档都表明,当我为IUnknown实现QueryInterface() 指向其中一个接口:

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
}

为什么我不能复制

if( iid == __uuidof( IUnknown ) ) {
     *ppv = this;
     //call Addref(), return S_OK
}

如果我做后者,我将违反对同一对象的QueryInterface()的任何调用必须返回完全相同的值的要求。

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.

我不太懂吗。它们的意思是,如果IInterface2的QI()和通过指针调用QueryInterface()C ++将传递这个稍微不同于IInterface2的QI(),因为C ++将每次使 指向子对象?

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?

推荐答案

问题是 * ppv 通常是 void * - 直接赋值这个 > this 指针并给出 * ppv 的值(因为所有指针都可以转换为 void * )。

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*).

这不是单继承的问题,因为使用单继承,所有类的基指针总是相同的(因为vtable只是扩展对于派生类)。

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).

然而 - 对于多重继承,你最终得到多个基本指针,这取决于你正在谈论的类的'视图'!这样做的原因是,使用多重继承,你不能只是扩展vtable - 你需要多个vtables根据你正在谈论的分支。

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.

所以你需要以便确保编译器将正确的基本指针(用于正确的vtable)放入 * ppv

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.

以下是单继承的示例:

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:

vtable for A:

[0] fa0
[1] fa1

B:

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

注意,如果你有 B vtable,你把它看作一个 A vtable它只是工作 - A 的成员的偏移量

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.

下面是一个使用多继承的示例(使用 A 和<$ c $的定义) c> B 从上面)(注意:只是一个例子 - 实现可能不同):

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:

vtable for C:

[0] fc0
[1] fc1

D:

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

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

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!

当在 D 上调用 c0 时,编译器会做的事情实际上是传递一个假的这个指针有一个vtable,它看起来像是 C

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.

因此,当你在 D 上调用 C 函数时,需要调整vtable以指向 D 对象(在 @C vtable)。

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.

这篇关于为什么我需要一个明确的upcast当实现QueryInterface()在一个具有多个接口的对象()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆