g ++ -fdump-class-hierarchy输出中的第一个(int(*)(...))0 vtable条目是什么?

Aqu*_*irl 49 c++ gcc virtual-functions vtable

对于此代码:

class B1{
public:  
  virtual void f1() {}  
};

class D : public B1 {
public:
  void f1() {}
};

int main () {
    B1 *b1 = new B1();
    D  *d  = new D();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译后,我得到的vtable g++ -fdump-class-hierarchy是:

Vtable for B1
B1::_ZTV2B1: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI2B1)
16    B1::f1


Vtable for D
D::_ZTV1D: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1D)
16    D::f1
Run Code Online (Sandbox Code Playgroud)

我无法理解像(int()(...))0*这样的条目对应的内容.当然它意味着类似,它是一个函数,它返回一个int并获取无限数量的参数,我不明白任何事情.这个函数指针对应哪个函数?你怎么知道的?我是64位机器.

第二个函数指针的末尾有一个地址?对应的对象是谁?

编辑

我使用的编译器是g ++:

g++ -v
Using built-in specs.
Target: x86_64-suse-linux
Configured with: ../configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man --libdir=/usr/lib64 --libexecdir=/usr/lib64 --enable-languages=c,c++,objc,fortran,obj-c++,java,ada --enable-checking=release --with-gxx-include-dir=/usr/include/c++/4.4 --enable-ssp --disable-libssp --with-bugurl=http://bugs.opensuse.org/ --with-pkgversion='SUSE Linux' --disable-libgcj --disable-libmudflap --with-slibdir=/lib64 --with-system-zlib --enable-__cxa_atexit --enable-libstdcxx-allocator=new --disable-libstdcxx-pch --enable-version-specific-runtime-libs --program-suffix=-4.4 --enable-linux-futex --without-system-libunwind --with-arch-32=i586 --with-tune=generic --build=x86_64-suse-linux
Thread model: posix
*gcc version 4.4.1 [gcc-4_4-branch revision 150839] (SUSE Linux)*
Run Code Online (Sandbox Code Playgroud)

Blu*_*eft 52

这些是偏移到顶部(多重继承所需)和typeinfo(RTTI)指针.

Itanium ABI (您没有使用Itanium编译器,但他们对此的描述非常好):

顶部开始偏移量将位于对象顶部的位移从虚拟表指针的对象内的位置(作为ptrdiff_t)进行处理.它始终存在.偏移量提供了一种从具有虚拟表指针的任何基础子对象中查找对象顶部的方法.这对于dynamic_cast尤其必要.
(在完整的对象虚拟表中,因此在其所有主要基本虚拟表中,此偏移的值将为零.[...])

所属类别指针指向用于RTTI的所属类别的对象.它始终存在.给定类的每个虚拟表中的所有条目必须指向相同的typeinfo对象.typeinfo相等的正确实现是检查指针相等性,除了指针(直接或间接)到不完整类型.typeinfo指针是多态类的有效指针,即具有虚函数的类,对于非多态类是零.


偏移到顶部更详细 (根据要求)

假设您有一个派生类D派生自基类,B1.当您尝试将D实例强制转换为什么时会发生什么B1?由于获取B1对象的函数不知道任何内容D,因此Dvtable的一部分也必须是有效的B1vtable.这很容易 - 只需使Dvtable 的开头看起来像B1vtable,然后添加我们需要的任何其他条目.希望一个人B1会感到高兴的功能,因为他们不会使用vtable的任何部分超出他们期望的范围B1.

但是,如果D现在也会从中得到B2什么?指向Dvtable 的指针不能同时是有效的B1vtable 有效的B2vtable!编译器通过B2在我们的组合D/B1vtable 的末尾附加一个单独的vtable来解决这个问题,并在我们尝试从a转换D为a 时手动调整vtable-pointer B2.

然而,这会导致一个新问题 - 当我们尝试从a 转换回来时会发生什么?编译器不能随便调整虚表指针向后由其先前调整的指针一样量,因为它实际上并不知道肯定的是,我们给它的对象是类的!特别是,必须能够判断我们的对象是否属于类型.为此,它需要访问对象的RTTI,并认为,它需要知道原始对象的虚函数表的开始.这是偏移到顶部值的目的 - 它给了我们原始对象的vtable开始的偏移量,我们获得了对象的RTTI,而C++的复仇之神允许我们的作物在另一个季节生长.B2DB2Ddynamic_cast<D>() D

这个页面有一些很好的vtable布局示例(在表1c下).请注意,由于使用了虚拟继承,它们稍微复杂一些,这会为每个子类的vtable添加额外的偏移量.

  • @Anisha Kaul:嗯,它几乎在你的问题中说:指向`f1`的指针被存储为该特定实现中vtable中的第三个元素:`16 B1 :: f1`. (3认同)
  • 偏向顶部稍微不那么简单地放入外行的话......基本上在多重继承的情况下,对不同的基础进行转换将导致指向不同子对象的指针,这些子对象从指向最派生对象的指针偏移.该偏移量允许您(嗯,`dynamic_cast`)恢复该偏移量并获得指向对象开头的指针. (2认同)
  • `struct A {int x; }; struct B {int y; virtual void foo(){}}; struct C:A,B {}; int main(){C c; C*cp =&c; A*ap =&c; B*bp =&c; cout << cp <<","<< ap <<","<< bp << std :: endl; 这个测试显示了向上转换如何影响指针.现在直观地能够从"B*"向下转换为"C*"编译器需要`typeinfo`对象知道转换是可能的,并且偏移到base以便能够将指针移回原始位置.(事实上​​,对于那种情况并不严格需要,但是在"虚拟"继承的情况下,它是,但这要复杂得多. (2认同)