什么是g ++ -fdump-class-hierarchy输出中的第一个(int(*)(...))0 vtable条目? [英] What is the first (int (*)(...))0 vtable entry in the output of g++ -fdump-class-hierarchy?

查看:1105
本文介绍了什么是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;
}

编译后,vtable可以用 g ++ - fdump-class-hierarchy 是:

  V表为B1 
B1 :: _ ZTV2B1: 3u条目
0(int(*)(...))0
8(int(*)(...))(& _ZTI2B1)
16 B1 :: f1


V表为D
D :: _ ZTV1D:3u条目
0(int(*)(...))0
8 *)(...))(& _ZTI1D)
16 D :: f1

我不明白什么是像(int()(...))0 *对应的条目。当然,这意味着类似的东西,它是一个函数返回一个int,并采取无限数量的参数,我不明白任何进一步。
这个函数指针对应到哪个函数?你怎么知道的? Mine是一个64位的机器。



第二个函数指针有一个地址在结尾??

$ p

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

  g ++ -v 
使用内置规格。
目标:x86_64-suse-linux
配置为:../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
线程模型:posix
* gcc版本4.4.1 [gcc-4_4-branch revision 150839](SUSE Linux)*


解决方案

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



Itanium ABI (您未使用Itanium编译器,但它们的描述非常好)


从作为ptrdiff_t的寻址该虚拟表的虚拟表指针的对象内的位置到对象顶部的位移。它总是存在。偏移量提供了一种从带有虚拟表指针的任何基本子对象中找到对象的顶部的方法。这对于dynamic_cast尤其必要。

(在一个完整的对象虚拟表中,因此在所有的主基本虚拟表中,这个偏移的值将为零。 ])



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







更详细地偏移到顶部 em>(按请求)



假设你有一个派生类 D 基类, B1 。当您尝试投入 D 实例键入 B1 时会发生什么?由于使用 B1 对象的函数不了解 D 的任何内容, D vtable也必须是有效的 B1 vtable。这很容易 - 只需让 D vtable的开头看起来像一个 B1 vtable,我们需要的条目。期望 B1 的函数将很高兴,因为他们不会使用vtable的任何部分,而不是他们期望的 B1



但是,如果 D 现在 B2 ?指向 D vtable的指针不能为两者有效的 B1 vtable 有效的 B2 vtable!编译器通过在组合的 D / B1 vtable的末尾附加一个单独的 B2 vtable来解决这个问题,当我们尝试从 D 转换到 B2 时,手动指向vtable指针。



但是,这会导致一个新的问题 - 当我们尝试从 B2 转换回到 D ?编译器不能只是将vtable指针向后调整与之前调整指针相同的量,因为它实际上并不知道确定 B2 对象,我们给它的类型 D !特别是 dynamic_cast< D>() 必须能够判断我们的对象是否是 D 类型。为此,它需要访问对象的RTTI,并且对于 ,它需要知道原始对象的vtable的起始位置。这是偏移到顶值的目的 - 它给我们偏移到原始对象的vtable的开始,我们得到我们的对象的RTTI,并且复仇的C ++的神允许我们的作物增长另一个季节。 p>

此网页 vtable布局的一些好例子(在表1c 下)。请注意,由于使用虚拟继承,它们稍微复杂一些,这会为每个子项的vtable增加一个额外的偏移量类。


For this code:

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;
}

After compilation, the vtable I get with g++ -fdump-class-hierarchy is:

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

I failed to understand what do the entries like (int ()(...))0* correspond to. Of course it means something like, it is a function which returns an int and takes unlimited number of arguments, I don't understand anything further. To which function does this function pointer correspond to? and how do you know that? Mine is a 64 bit machine.

The second function pointer has an address associated at end?? To whom does that correspond to?

EDIT

The compiler, I use is 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)*

解决方案

Those are the offset-to-top (needed for multiple inheritence) and typeinfo (RTTI) pointers.

From the Itanium ABI (you are not using the Itanium compiler, but their description of this is really good):

The offset to top holds the displacement to the top of the object from the location within the object of the virtual table pointer that addresses this virtual table, as a ptrdiff_t. It is always present. The offset provides a way to find the top of the object from any base subobject with a virtual table pointer. This is necessary for dynamic_cast in particular.
(In a complete object virtual table, and therefore in all of its primary base virtual tables, the value of this offset will be zero. [...])

The typeinfo pointer points to the typeinfo object used for RTTI. It is always present. All entries in each of the virtual tables for a given class must point to the same typeinfo object. A correct implementation of typeinfo equality is to check pointer equality, except for pointers (directly or indirectly) to incomplete types. The typeinfo pointer is a valid pointer for polymorphic classes, i.e. those with virtual functions, and is zero for non-polymorphic classes.


Offset-to-top in more detail (by request)

Let's say you have a derived class D that derives from a base class, B1. What happens when you try to cast a D instance to type B1? Since functions that take a B1 object don't know anything about D, part of the D vtable must also be a valid B1 vtable. This is easy enough - just make the start of the D vtable look like a B1 vtable, and add on any additional entries we need after that. Functions expecting a B1 will be happy, because they won't use any part of the vtable beyond what they're expecting for a B1.

However, what happens if D now also derives from B2? The pointer to the D vtable can't be both a valid B1 vtable and a valid B2 vtable! The compiler solves this by appending a separate B2 vtable to the end of our combined D/B1 vtable, and adjusts the vtable-pointer manually when we try to cast from a D to a B2.

However, this leads to a new problem - what happens when we try to cast back from a B2 to a D? The compiler can't just adjust the vtable-pointer backwards by the same amount it adjusted the pointer previously, because it doesn't actually know for sure that the B2 object we're giving it is of type D! In particular, dynamic_cast<D>() must be able to tell if our object is or isn't of type D. For that, it needs to access the object's RTTI, and for that, it needs to know where the start of the original object's vtable is. This is the purpose of the offset-to-top value - it gives us the offset to the start of the original object's vtable, we get our object's RTTI, and the vengeful god of C++ allows our crops to grow for another season.

This page has some good examples of vtable layouts (under Table 1c). Note that they are slightly more complicated due to the use of virtual inheritance, which adds an extra offset to the vtable of each child class.

这篇关于什么是g ++ -fdump-class-hierarchy输出中的第一个(int(*)(...))0 vtable条目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆