Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/reporting-services/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 关于“的问题”;此指针调节器;在C++;对象布局_C++_Visual C++ - Fatal编程技术网

C++ 关于“的问题”;此指针调节器;在C++;对象布局

C++ 关于“的问题”;此指针调节器;在C++;对象布局,c++,visual-c++,C++,Visual C++,我有点被一个问题弄糊涂了:在什么情况下MS VC++编译器会生成一个新的代码?请注意,此调整器不一定处于默认状态。下面是我的测试代码 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:publi

我有点被一个问题弄糊涂了:在什么情况下MS VC++编译器会生成一个新的代码?请注意,此调整器不一定处于默认状态。下面是我的测试代码

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
不在thunk此调节器中

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

  • 编译器将在什么情况下生成此调整器的


非常感谢。

< P>我已经做C++十多年了,从来没有必要担心过这些。但是,对于不在结构开头的类,似乎在MI期间“此调整器”开始发挥作用。

这是一个虚拟步骤


将该表视为虚拟vtable(而不仅仅是vtable)。虚拟步骤需要一些计算:给定一个指针,计算vtable。(或者,在本例中,给定一个vtable,计算另一个vtable。)该计算由thunk执行。但如果不需要执行虚拟操作,则不需要查找其他vtable,也不需要执行计算,因此不需要thunk。这就是为什么有些步骤仅仅是补偿,而另一些步骤是作为thunk实现的。这是虚拟的虚拟步骤。

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

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

struct Derived : Base { 
    int y;
    virtual void f() {}
    virtual ~Derived() {}
};
在典型情况下,这将通过为每个类提供vtable来实现,并使用(隐藏的)vtable指针创建每个对象。每个对象(基类或派生类)的vtable指针将在结构中具有相同偏移量的vtable指针,并且每个对象将包含指向虚拟表中相同偏移量的虚拟函数(
f
和dtor)的指针

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

void g(Base&b) { 
    b.f();
}
由于Base和Derived(以及Base的任何其他派生)都以相同的方式排列vtable,并且指向vtable的指针在结构中的偏移量相同,因此编译器可以为此生成完全相同的代码,而不管它是处理Base、Derived还是从Base派生的其他内容

但是,当您将多重继承添加到混合中时,这种情况会发生变化。特别是,您不能安排所有对象,使指向vtable的指针在每个对象中始终处于相同的偏移量,原因很简单,一个从两个基类派生的对象将(可能)具有指向两个单独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的指针作为这里的主要例子,但事实并非如此,实际上,即使在任何类中都没有虚拟函数,仍然需要访问每个基类的数据,这需要相同的调整。

< P>您也可以回顾一下我在MSC++对象M上的文章。应用程序,“C++:引擎盖下”,仍然可用


愉快的黑客行为!

谢谢你的回复。是的,我正在学习COM。COM规程不允许虚拟存在。在我研究这一点时,我想到了上面的问题。我想知道编译器为什么要费心生成“此调整器”这有必要吗?这有什么语义含义吗?谢谢杰瑞。我会消化你的回答。:)非常感谢简。我已经下载了它,并简要地看了一下。这很有帮助。我会在这上面花更多的时间。:)