什么是g ++ -fdump-class-hierarchy的输出中的第一个(int(*)(…))0 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; }
编译之后,我用g++ -fdump-class-hierarchy
获得的vtable是:
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
我不明白像(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)*
这些是偏移到顶部(需要多重inheritance)和typeinfo(RTTI)指针。
从Itanium ABI (您没有使用Itanium编译器,但是它们的描述非常好) :
顶部的偏移量将位于该虚拟表的虚拟表指针的对象内的位置的位移保持为对象的顶部,作为ptrdiff_t。 它始终存在。 偏移量提供了一种方法,可以使用虚拟表指针从任何基本子对象中查找对象的顶部。 这对于dynamic_cast尤其是必需的。
(在一个完整的对象虚拟表中,因此在其所有主要的基本虚拟表中,这个偏移量的值将为零[…])typeinfo指针指向用于RTTI的typeinfo对象。 它始终存在。 给定类的每个虚拟表中的所有条目都必须指向同一个typeinfo对象。 typeinfo相等的正确实现是检查指针是否相等,除了指针(直接或间接)为不完整types。 typeinfo指针是多态类的有效指针,即具有虚函数的指针,非多态类为零。
更详细的偏置 (按要求)
假设你有一个从基类B1
派生的派生类D
当您尝试将D
实例投入B1
types时会发生什么? 因为带有B1
对象的函数对D
没有任何了解,所以D
vtable的一部分也必须是一个有效的B1
vtable。 这很简单 – 只要让D
vtable的开始看起来像一个B1
vtable,然后添加我们之后需要的任何附加条目。 期望B1
函数将很高兴,因为它们不会使用任何超出他们期望的B1
。
但是,如果D
现在也来自B2
,会发生什么? 指向D
表的指针既不是有效的B1
表格,也不是有效的B2
表格! 编译器通过在我们的D/B1
组合的vtable的末尾附加一个单独的B2
vtable来解决这个问题,并且当我们试图从D
到B2
时手动调整vtable-pointer。
然而,这导致了一个新的问题 – 当我们试图从B2
退回到D
什么? 编译器不能仅仅调整vtable-pointer与之前调整指针相同的量,因为它实际上并不知道我们赋予它的B2
对象是D
types的! 特别是, dynamic_cast<D>()
必须能够判断我们的对象是否是D
types的。 为此,它需要访问对象的RTTI,为此,它需要知道原始对象的vtable的起始位置。 这是offset-to-top值的目的 – 它给了我们对原始对象的vtable的开始的偏移,我们得到了我们的对象的RTTI,C ++的复仇之神允许我们的作物生长另一个季节。
这个页面有一些很好的vtable布局的例子(在表1c中 )。 请注意,由于使用了虚拟inheritance ,它们会稍微复杂一些,这会为每个子类的vtable增加一个额外的偏移量。
也许第一个入口是虚拟析构函数,第二个入口是RTTI支持? 但这只是一个猜测。