设为首页 加入收藏

TOP

C++对象模型:g++的实现(五)(四)
2023-07-23 13:33:45 】 浏览:92
Tags:
t2D::allAddOne(); m_z += 1; } virtual int z() const override { return m_z; } private: int m_z; }; int main () { Point3D* p3d = new Point3D(1, 2, 3); Point2D* p2d = p3d; p2d->allAddOne(); int z = p2d->z(); }

我们先使用-fdump-class-hierarchy查看类的信息:

Vtable for Point2D
Point2D::_ZTV7Point2D: 6 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI7Point2D)
16    (int (*)(...))Point2D::~Point2D
24    (int (*)(...))Point2D::~Point2D
32    (int (*)(...))Point2D::allAddOne
40    (int (*)(...))Point2D::z

Class Point2D
   size=16 align=8
   base size=16 base align=8
Point2D (0x0x7ff517ae7960) 0
    vptr=((& Point2D::_ZTV7Point2D) + 16)

Vtable for Point3D
Point3D::_ZTV7Point3D: 16 entries
0     16
8     (int (*)(...))0
16    (int (*)(...))(& _ZTI7Point3D)
24    (int (*)(...))Point3D::~Point3D
32    (int (*)(...))Point3D::~Point3D
40    (int (*)(...))Point3D::allAddOne
48    (int (*)(...))Point3D::z
56    18446744073709551600
64    18446744073709551600
72    18446744073709551600
80    (int (*)(...))-16
88    (int (*)(...))(& _ZTI7Point3D)
96    (int (*)(...))Point3D::_ZTv0_n24_N7Point3DD1Ev
104   (int (*)(...))Point3D::_ZTv0_n24_N7Point3DD0Ev
112   (int (*)(...))Point3D::_ZTv0_n32_N7Point3D9allAddOneEv
120   (int (*)(...))Point3D::_ZTv0_n40_NK7Point3D1zEv

VTT for Point3D
Point3D::_ZTT7Point3D: 2 entries
0     ((& Point3D::_ZTV7Point3D) + 24)
8     ((& Point3D::_ZTV7Point3D) + 96)

Class Point3D
   size=32 align=8
   base size=12 base align=8
Point3D (0x0x7ff51797d1a0) 0
    vptridx=0 vptr=((& Point3D::_ZTV7Point3D) + 24)
  Point2D (0x0x7ff517ae7de0) 16 virtual
      vptridx=8 vbaseoffset=-24 vptr=((& Point3D::_ZTV7Point3D) + 96)

Point3D对象的结构还是比较简单的,如下:

(gdb) x/8xw p3d
0x8414e70:      0x08201cd0      0x00000000      0x00000003      0x00000000
0x8414e80:      0x08201d18      0x00000000      0x00000001      0x00000002

很明显0x08201cd0是Point3D新增的虚表指针,结合类信息,我们知道其指向了((& Point3D::_ZTV7Point3D) + 24);而0x08201d18是继承自虚基类的放虚表指针的地方,只不过这里放了Derived类自己的虚表指针,其指向了((& Point3D::_ZTV7Point3D) + 96)。
我们关注的重点是虚基类的虚函数表和其中虚函数的实现:虚函数表中放的是什么的地址?不像没有虚基类的多重继承那样各个对象的偏移是一定的,(在只有指针或引用的情况下)虚继承下虚基类的偏移是运行时才能知道的,其中的虚函数又是如何调整this指针的呢?

(gdb) x/4ag 0x08201d18
0x8201d18 <_ZTV7Point3D+96>:    0x8000ba6 <_ZTv0_n24_N7Point3DD1Ev>     0x8000bdb <_ZTv0_n24_N7Point3DD0Ev>
0x8201d28 <_ZTV7Point3D+112>:   0x8000c24 <_ZTv0_n32_N7Point3D9allAddOneEv>     0x8000c3f <_ZTv0_n40_NK7Point3D1zEv>

正如类信息中展示的那样,虚基类的虚表中放置的正是这几个函数名字,但这几个函数是什么呢?我们使用c++filt看一下:

$ c++filt _ZTv0_n24_N7Point3DD1Ev
virtual thunk to Point3D::~Point3D()
$ c++filt _ZTv0_n24_N7Point3DD0Ev
virtual thunk to Point3D::~Point3D()
$ c++filt _ZTv0_n32_N7Point3D9allAddOneEv
virtual thunk to Point3D::allAddOne()
$ c++filt _ZTv0_n40_NK7Point3D1zEv
virtual thunk to Point3D::z() const

可以看到他们被称为virtual thunk,看来是和thunk相似的技术,用来调整this指针和返回值,我们来看看其内部是怎么运行的:
虚表指针指向的第一个表项指向空间的反汇编
和我们前面讨论的thunk非常像,都是调整this指针,只是前面的thunk里this指针调整的值是固定的,而这里this指针调整的值是动态的放在vptr[-3]处,我们再看一下这里放的是什么,我们直接看g++生成的类信息,虚表指针是指向((& Point3D::_ZTV7Point3D) + 96),那vptr[-3]就应该是((& Point3D::_ZTV7Point3D) + 72)放的东西了,可以看到是18446744073709551600,把这个值当作一个long类型的值的话正好是-16,这不就是从Point2D*类型转化为Point3D*类型需要减的值嘛(因为Point2D在Point3D类的实体中偏移为16)。我们再检查一下其他的virtual thunk是不是也是一样?
虚表指针指向的第二个表项指向空间的反汇编
嗯,没问题,再看看下一个:
虚表指针指向的第三个表项指向空间的反汇编
不好,出现不一样了,这次偏移是vptr[-4]这里,也就是((& Point3D::_ZTV7Point3D) + 64)放的东西,可以看到是1844674

首页 上一页 1 2 3 4 下一页 尾页 4/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇std::weak_ptr<void>绑定到.. 下一篇C++对象模型:g++的实现(六)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目