为什么我们需要“this pointer adjustor thunk”? [英] Why do we need "this pointer adjustor thunk"?

查看:192
本文介绍了为什么我们需要“this pointer adjustor thunk”?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在这里阅读了关于调整器a>。这里有一些引文:

I read about adjustor thunk from here. Here's some quotation:


现在,只有一个QueryInterface
方法,但有两个条目,一个
为每个vtable。请记住,vtable中的每个
函数接收
对应的接口指针作为其
this参数。这只是罚款
QueryInterface(1);它的接口
指针与对象的
接口指针相同。但是对于QueryInterface(2)这是坏消息
,因为它的
接口指针是q,而不是p。

Now, there is only one QueryInterface method, but there are two entries, one for each vtable. Remember that each function in a vtable receives the corresponding interface pointer as its "this" parameter. That's just fine for QueryInterface (1); its interface pointer is the same as the object's interface pointer. But that's bad news for QueryInterface (2), since its interface pointer is q, not p.

这是调整器thunk
in。

This is where the adjustor thunks come in.

我想知道为什么 vtable中的每个函数都会接收相应的接口指针,此参数?是接口方法用来定位对象实例中数据成员的唯一线索(基地址)吗?

I am wondering why "each function in a vtable receives the corresponding interface pointer as its "this" parameter"? Is it the only clue(base address) used by the interface method to locate data members within the object instance?

这是我的最新理解:

其实我的问题不是 参数,但是为什么我们必须使用相应的接口指针作为 参数。

In fact, my question is not about the purpose of this parameter, but about why we have to use the corresponding interface pointer as the this parameter. Sorry for my vagueness.

除了在对象的布局中使用接口指针作为定位器/脚注 。当然还有其他方法,只要你是组件的实现者。

Besides using the interface pointer as a locator/foothold within an object's layout. There're of course other means to do that, as long as you are the implementer of the component.

但是这不是我们的组件的客户端的情况。

But this is not the case for the clients of our component.

当组件以COM方式构建时,我们的组件的客户端对我们组件的内部构件一无所知。 客户端只能抓住接口指针,这是将作为参数传递到接口方法的指针。根据此 期望,编译器不得不基于此特定 指针生成接口方法的代码< 。

When the component is built in COM way, clients of our component know nothing about the internals of our component. Clients can only take hold of the interface pointer, and this is the very pointer that will be passed into the interface method as the this parameter. Under this expectation, the compiler has no choice but to generate the interface method's code based on this specific this pointer.

因此,上述推理会导致以下结果:

So the above reasoning leads to the result that:


确保vtable中的每个函数
必须将
对应的接口指针接收为其
this 参数。

在this pointer adjustor thunk的情况下,单个QueryInterface()方法存在2个不同的条目,换句话说,2个不同的接口指针可以用来调用QueryInterface()方法,但是编译器只生成一个QueryInterface()方法的副本。因此,如果编译器选择其中一个接口作为this指针,我们需要将其他接口调整为所选择的接口。这是调整器thunk出生的。

In the case of "this pointer adjustor thunk", 2 different entries exist for a single QueryInterface() method, in other words, 2 different interface pointers could be used to invoke the QueryInterface() method, but the compiler only generate 1 copy of QueryInterface() method. So if one of the interfaces is chosen by the compiler as the this pointer, we need to adjust the other to the chosen one. This is what the this adjustor thunk is born for.

BTW-1,如果编译器可以生成2个不同的QueryInterface()方法的实例怎么办?每一个都基于相应的接口指针。这不需要调整器thunk,但它会占用更多的空间来存储额外的但类似的代码。

BTW-1, what if the compiler can generate 2 different instances of QueryInterface() method? Each one based on the corresponding interface pointer. This won't need the adjustor thunk, but it would take more space to store the extra but similar code.

BTW-2:似乎有时一个问题缺乏合理的解释从实现者的角度,但可以从用户的视角更好地理解。

BTW-2: it seems that sometimes a question lacks a reasonable explanation from the implementer's point of view, but could be better understood from the user's pointer of view.

推荐答案

从问题中取出COM部分, this adjustor thunk是一段代码,它确保每个函数获取指向具体类型的子对象的这个指针。

Taking away the COM part from the question, the this pointer adjustor thunk is a piece of code that makes sure that each function gets a this pointer pointing to the subobject of the concrete type. The issue comes up with multiple inheritance, where the base and derived objects are not aligned.

考虑下面的代码:

struct base {
   int value;
   virtual void foo() { std::cout << value << std::endl; }
   virtual void bar() { std::cout << value << std::endl; }
};
struct offset {
   char space[10];
};
struct derived : offset, base {
   int dvalue;
   virtual void foo() { std::cout << value << "," << dvalue << std::endl; }
};

(并忽略缺少初始化)。 中的基础子对象不与对象的开头对齐,因为有一个 offset [1] 之间。当指向派生的指针被强制转换为指向 base 的指针时(包括隐式转换,而不是重新解释转换UB和潜在死亡),指针的值被偏移,使得假定对象(void *)d!=(void *)((base *)d) code> d

(And disregard the lack of initialization). The base sub object in derived is not aligned with the start of the object, as there is a offset in between[1]. When a pointer to derived is casted to a pointer to base (including implicit casts, but not reinterpret casts that would cause UB and potential death) the value of the pointer is offsetted so that (void*)d != (void*)((base*)d) for an assumed object d of type derived.

derived d;
base * b = &d; // This generates an offset
b->bar();
b->foo();

问题出现在从 base 指针或引用。如果虚拟分派机制发现最终超控程序在 base 中,则指针 this 必须引用<$如 b-> bar 中的 base 对象,其中隐式 this 指针是存储在 b 中的相同地址。现在,如果最终重写在派生类中,与 b-> foo()一样, this (在这种情况下派生)的类型的子对象的开始对齐。

The issue comes when a function is called from a base pointer or reference. If the virtual dispatch mechanism finds that the final overrider is in base, then the pointer this must refer to the base object, as in b->bar, where the implicit this pointer is the same address stored in b. Now if the final overrider is in a derived class, as with b->foo() the this pointer has to be aligned with the beginning of the sub object of the type where the final overrider is found (in this case derived).

编译器所做的是创建一个中间代码段。当调用虚拟分派机制时,并且在分派到 derived :: foo 之前,中间调用接受这个指针,将偏移量减去派生对象的开头。此操作与向下转换 static_cast 相同。请记住,此时,指针的类型为 base ,因此最初被偏移,这有效地返回原始值& d

What the compiler does is creating an intermediate piece of code. When the virtual dispatch mechanism is called, and before dispatching to derived::foo the intermediate call takes the this pointer and substracts the offset to the beginning of the derived object. This operation is the same as a downcast static_cast<derived*>(this). Remember that at this point, the this pointer is of type base, so it was initially offsetted, and this effectively returns the original value &d.

[1] 即使在 interfaces 的情况下 - 在Java / C#sense:只定义虚拟方法的类 - 因为他们需要存储一个表到该接口的vtable。

[1]There is an offset even in the case of interfaces --in the Java/C# sense: classes defining only virtual methods-- as they need to store a table to that interface's vtable.

这篇关于为什么我们需要“this pointer adjustor thunk”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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