Why do we need “this pointer adjustor thunk”?

后端 未结 5 2030
予麋鹿
予麋鹿 2020-12-16 21:23

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

Now, there is only one QueryInterface method, but there are two entries, one for ea

5条回答
  •  我在风中等你
    2020-12-16 22:08

    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.

    Consider the following code:

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

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

    Now condider the usage:

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

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

    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(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]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.

提交回复
热议问题