g ++

编程入门 行业动态 更新时间:2024-10-23 04:42:06
g ++ -fdump-class-hierarchy的输出中的第一个(int(*)(...))0 vtable条目是什么?(What is the first (int (*)(…))0 vtable entry in the output of g++ -fdump-class-hierarchy?)

对于这段代码:

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)*

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)*

最满意答案

那些是偏移到顶部(需要多个继承)和typeinfo(RTTI)指针。

从Itanium ABI (您不使用Itanium编译器,但它们的描述真的很好)

到顶部偏移将位置从位于虚拟表指针的对象内的位置放置到对象的顶部,该对象位于作为ptrdiff_t的虚拟表中。 它总是存在。 偏移量提供了一种使用虚拟表指针从任何基本子对象中找到对象顶部的方法。 这对于dynamic_cast尤其是必需的。 (在完整的对象虚拟表中,因此在其所有主基础虚拟表中,此偏移量的值将为零。[...])

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


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

假设你有一个从基类B1派生的派生类D 当您尝试将D实例转换为B1类型时会发生什么? 由于使用B1对象的函数不了解D任何内容,所以D vtable的一部分也必须是有效的B1 vtable。 这很简单 - 只需使D vtable的开始看起来像一个B1 vtable,并添加我们之后需要的任何其他条目。 期望B1功能将会很快乐,因为他们不会使用vtable的任何部分超出他们对B1期望。

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

但是,这会导致一个新的问题 - 当我们尝试从B2转向D什么? 编译器不能像以前调整指针一样向后调整vtable-pointer,因为它实际上并不知道我们给它的B2对象是D类型! 特别地, dynamic_cast<D>() 必须能够判断我们的对象是否为D 。 为此,它需要访问对象的RTTI,为此,它需要知道原始对象的vtable的开始位置。 这是偏移到顶部值的目的 - 它给我们偏移了原始对象的vtable的开始,我们得到了我们的对象的RTTI,而C ++的复仇之神使我们的作物能够再次增长。

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

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.

更多推荐

本文发布于:2023-07-24 20:07:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1250552.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!