【问题标题】:vtable and polymorphism - offset of a functionvtable 和多态性 - 函数的偏移量
【发布时间】:2014-09-24 16:32:41
【问题描述】:

如果我理解正确的话,类定义会在 vtable 中强加虚函数的特定顺序,因此已知给定函数与表的开头有一定的偏移量。但是,我不明白这如何与多态性一起使用。

class B1 {
  virtual void funcB1();
};

class B2 {
  virtual void funcB2() {}
};

class D : public B1, public B2 {
  virtual void funcB1() {}
  virtual void funcB2() {}
};

void main(...) {
  B1 *b1 = new D();
  B2 *b2 = new D();
  B1 *realB1 = new B1();
  B2 *realB2 = new B2();

  b1->funcB1();
  b2->funcB2();
  realB1->funcB1();
  realB2->funcB2();
}

生成的代码如何知道如何访问不同偏移量的funcB2?

【问题讨论】:

    标签: c++ polymorphism multiple-inheritance virtual-functions vtable


    【解决方案1】:

    当您从两个基类组成一个类时,每个部分在结果类中由一个功能齐全的块表示,并带有自己的指向 vtable 的指针。这就是生成的代码知道调用什么函数的方式:将D的指针转换为B1B2会产生不同的指针,因此生成的代码可以使用相同的偏移量进入虚拟表。

    D *d = new D();
    B1 *b1 = dynamic_cast<B1*>(d);
    B2 *b2 = dynamic_cast<B2*>(d);
    printf("%p %p %p", (void*)d, (void*)b1, (void*)b2);
    

    这个produces the following output on ideone

    0x91c7008 0x91c7008 0x91c700c
    

    注意D*B1* 如何打印相同的值,而B2* 打印不同的值。当您调用b2-&gt;funcB2() 时,指针b2 已经指向D 对象的不同部分,该部分指向不同的vtable(具有B2 布局的vtable),因此生成的代码不会在您的示例中,需要为 b2realB2 做任何不同的事情。

    【讨论】:

    • 当我调用一个接受 B2* 的函数时,编译器如何知道它得到的是实际的 B2 还是 D?它是否添加运行时代码来检查,并相应地计算偏移量?
    • @Dorfl 当编译器看到你调用一个接受B2*的函数时,它会在调用点静默插入D*B2*的转换,以便指针指向函数看起来和行为就好像它是一个指向 B2 的纯指针。
    • 终于搞定了!谢谢!抱歉,我还不能投票给你,但我没有所需的声誉。
    • 顺便说一句,我只是想确定一下——当我从 B2* 转换回 D* 时,它是否会添加相反的偏移量以返回 D 的表应该在的位置?
    • @Dorfl 这是一个很好的问题!答案是“是”(here is a modified demo)。
    【解决方案2】:

    通常D 对象将有两个 vtable 指针,每个基类一个。这确实无法避免,因为它必须包含每个基类的相同二进制布局。每当您从一种类型转换为另一种类型时,编译器都会插入指针修正 - 如果您在转换为每个基类后打印指针地址,您会发现它们是不同的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多