c++ - 关于C++对象布局中 "this pointer adjustor"的问题

标签 c++ visual-c++

我对一个问题有点困惑:MS VC++ 编译器在什么情况下会生成 this adjustor ?请注意,此调节器 不一定在 thunk 中.下面是我的测试代码。

class myIUnknown
{
public:
    virtual void IUnknown_method1(void)=0;
    virtual void IUnknown_method2(void)=0;
    int data_unknown_1;
    int data_unknown_2;
};


class BaseX:public myIUnknown
{
public:
    BaseX(int);
    virtual void base_x_method1(void)=0;
    virtual void base_x_method2(void)=0;
    int data_base_x;
    int data_unknown_1;
    int data_unknown_2;
};

class BaseY:public myIUnknown
{
public:
    BaseY(int);
    virtual void base_y_method1(void);
    virtual void base_y_method2(void)=0;
    int data_base_y;
    int data_unknown_1;
    int data_unknown_2;
};

class ClassA:public BaseX, public BaseY
{
public:
    ClassA(void);
    //myIUnknown
    void IUnknown_method1(void);
    void IUnknown_method2(void);
    //baseX
    void base_x_method1(void) ;
    void base_x_method2(void) ;
    //baseY
    //void base_y_method1(void) ;
    void base_y_method2(void) ;
    virtual void class_a_method(void);
    int data_class_a;
    int data_unknown_1;
    int data_unknown_2;
};

对象布局如下:

1>  class ClassA    size(60):
1>      +---
1>      | +--- (base class BaseX)
1>      | | +--- (base class myIUnknown)
1>   0  | | | {vfptr}
1>   4  | | | data_unknown_1
1>   8  | | | data_unknown_2
1>      | | +---
1>  12  | | data_base_x
1>  16  | | data_unknown_1
1>  20  | | data_unknown_2
1>      | +---
1>      | +--- (base class BaseY)
1>      | | +--- (base class myIUnknown)
1>  24  | | | {vfptr}
1>  28  | | | data_unknown_1
1>  32  | | | data_unknown_2
1>      | | +---
1>  36  | | data_base_y
1>  40  | | data_unknown_1
1>  44  | | data_unknown_2
1>      | +---
1>  48  | data_class_a
1>  52  | data_unknown_1
1>  56  | data_unknown_2
1>      +---
1>  
1>  ClassA::$vftable@BaseX@:
1>      | &ClassA_meta
1>      |  0
1>   0  | &ClassA::IUnknown_method1
1>   1  | &ClassA::IUnknown_method2
1>   2  | &ClassA::base_x_method1
1>   3  | &ClassA::base_x_method2
1>   4  | &ClassA::class_a_method
1>  
1>  ClassA::$vftable@BaseY@:
1>      | -24
1>   0  | &thunk: this-=24; goto ClassA::IUnknown_method1 <=====in-thunk "this adjustor"
1>   1  | &thunk: this-=24; goto ClassA::IUnknown_method2 <=====in-thunk "this adjustor"
1>   2  | &BaseY::base_y_method1
1>   3  | &ClassA::base_y_method2
1>  
1>  ClassA::IUnknown_method1 this adjustor: 0
1>  ClassA::IUnknown_method2 this adjustor: 0
1>  ClassA::base_x_method1 this adjustor: 0
1>  ClassA::base_x_method2 this adjustor: 0
1>  ClassA::base_y_method2 this adjustor: 24  <============non-in-thunk "this adjustor"
1>  ClassA::class_a_method this adjustor: 0

我发现在下面的调用中,生成了这个pointer adjustors:

in-thunk 这个调节器:

pY->IUnknown_method1();//adjustor this! this-=24  pY-24==>pA
pY->IUnknown_method2();//adjustor this! this-=24  pY-24==>pA

non-in-thunk 这个调节器:

pA->base_y_method2();//adjustor this!   this+=24 pA+24==>pY
  • 谁能告诉我为什么编译器会在上述调用中产生这个调整器

  • 编译器在什么情况下会生成this adjustor

非常感谢。

最佳答案

也许最简单的方法是先思考单继承是如何(通常)在 C++ 中实现的。考虑一个包含至少一个虚函数的层次结构:

struct Base { 
    int x;
    virtual void f() {}
    virtual ~Base() {}
};

struct Derived : Base { 
    int y;
    virtual void f() {}
    virtual ~Derived() {}
};

在典型情况下,这将通过为每个类创建一个 vtable 来实现,并使用(隐藏的)vtable 指针创建每个对象。每个对象(Base 或 Derived 类)的 vtable 指针将在结构中的相同偏移处具有 vtable 指针,并且每个对象将包含指向虚函数的指针(f 和 dtor)在虚拟表中的相同偏移量。

现在,考虑这些类型的多态使用,例如:

void g(Base&b) { 
    b.f();
}

由于 Base 和 Derived(以及 Base 的任何其他派生)都具有以相同方式排列的 vtable,并且指向结构中相同偏移处的 vtable 的指针,编译器可以为此生成完全相同的代码,无论它是处理 Base、Derived 还是从 Base 派生的其他东西。

但是,当您将多重继承添加到组合中时,情况就会发生变化。特别是,您不能安排所有对象,以便指向 vtable 的指针在每个对象中始终处于相同的偏移量,原因很简单,从两个基类派生的对象将(可能) 有指向两个 单独的 vtable 的指针,它们显然不能在结构中处于相同的偏移量(即,你不能将两个不同的东西放在完全相同的位置)。为了适应这一点,您必须进行某种显式调整。每个多重派生类都必须有某种方式让编译器找到所有基类的虚表。考虑这样的事情:

struct Base1 { 
    virtual void f() { }
};

struct Base2 { 
    virtual void g() {}
};

class Derived1 : Base1, Base2 { 
    virtual void f() {}
    virtual void g() {}
};

class Derived2 : Base2, Base1 {
    virtual void f() {}
    virtual void g() {}
};

在典型情况下,编译器将按照您指定基类的相同顺序排列 vtable 指针,因此 Derived1 将有一个指向 Base1 的 vtable 的指针,后跟一个指向 Base2 的 vtable 的指针。 Derived2 将颠倒顺序。

现在,假设同一函数对 f() 进行多态调用,但将传递对 Base1、Derived1 或 Derived2 的引用。其中一个几乎不可避免地会以与其他指针不同的偏移量指向 Base1 的 vtable。这就是“this-adjustor”(或任何你喜欢调用它的东西)出现的地方——它为你尝试使用的基类找到正确的偏移量,所以当你访问该类的成员时,你会得到正确的数据。

请注意,虽然我在这里使用指向 vtable 的指针作为主要示例,但它不是唯一的可能性。事实上,即使您在任何类中都没有虚函数,您仍然需要访问每个基类的数据,这需要进行相同类型的调整。

关于c++ - 关于C++对象布局中 "this pointer adjustor"的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3128295/

相关文章:

c++ - 管理进程如何在登录用户中打开应用程序?

c++ - Visual Studio 中的条件断点 - 使用非基本数据类型的条件

windows - 针对 Mac/Unix 开发人员的 MS Windows 编程建议

c++ - 委派到私有(private)领域

c++ - 为什么 `std::string::reserve()` 没有保留我指定的确切空间量?

c++ - 枚举导致二进制更改与 Gcc 4.2.4 中的优化

c++ - Visual Studio 编译器错误 : a call to a delegating constructor shall be the only member-initializer

c++ - 如何在 C++ 上检测 Windows 是 32 位还是 64 位?

c++ - 在使用 cin 请求 int 时如何有效地防止用户输入?

c++ - 两个序列与 STL 的匹配数