博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++虚函数表分析
阅读量:6848 次
发布时间:2019-06-26

本文共 2240 字,大约阅读时间需要 7 分钟。

先看代码:

#include 
using namespace std;class Base {public: virtual void f() {cout<<"base::f"<

 

    都知道C++中的多态是用虚函数实现的: 子类覆盖父类的虚函数, 然后声明一个指向子类对象的父类指针, 如Base *b = new Derive();

当调用b->f()时, 调用的是子类的Derive::f()。 
这种机制内部由虚函数表实现,下面对虚函数表结构进行分析,并且用GDB验证。
 
1. 基础知识:
(1) 32位os 指针长度为4字节, 64位os 指针长度为8字节, 下面的分析环境为64位 linux & g++ 4.8.4.
(2) new一个对象时, 只为类中成员变量分配空间, 对象之间共享成员函数。

2. _vptr

    运行下上面的代码发现sizeof(Base) = 8, 说明编译器在类中自动添加了一个8字节的成员变量, 这个变量就是_vptr, 指向虚函数表的指针。
_vptr有些文章里说gcc是把它放在对象内存的末尾,VC是放在开始, 我编译是用的g++,验证了下是放在开始的:

验证代码:取对象a的地址与a第一个成员变量n的地址比较,如果不等,说明对象地址开始放的是_vptr. 也可以用gdb直接print a 会发现_vptr在开始

class A{public:      int n;      virtual void Foo(void){}};int main(){     A a;     char *p1 = reinterpret_cast
(&a); char *p2 = reinterpret_cast
(&a.n); if(p1 == p2) { cout<<"vPtr is in the end of class instance!"<

(3) 虚函数表

    包含虚函数的类才会有虚函数表, 同属于一个类的对象共享虚函数表, 但是有各自的_vptr.
    虚函数表实质是一个指针数组,里面存的是虚函数的函数指针。

Base中虚函数表结构:

Derive中虚函数表结构:

 

(4)验证

运行上面代码结果:
    size of Base: 8
    base::f
    derive::g
    base::h

说明Derive的虚函数表结构跟上面分析的是一致的:

    d对象的首地址就是vptr指针的地址-pvptr,
    取pvptr的值就是vptr-虚函数表的地址
    取vptr中[0][1][2]的值就是这三个函数的地址
    通过函数地址就直接可以运行三个虚函数了。
    函数表中Base::g()函数指针被Derive中的Derive::g()函数指针覆盖, 所以执行的时候是调用的Derive::g()

 

(5)多继承

 

 

附 GDB调试:(1) #生成带有调试信息的可执行文件g++ test.cpp -g -o test    (2) #载入testgdb test(3) #列出Base类代码(gdb) list Base1       #include 
23 using namespace std;45 class Base {6 public:7 virtual void f() {cout<<"base::f"<
and ends at 0x400ad4
.(gdb) info line 8Line 8 of "test.cpp" starts at address 0x400af2
and ends at 0x400afe
.(gdb) info line 9Line 9 of "test.cpp" starts at address 0x400b1c
and ends at 0x400b28
.(5)#列出Derive代码(gdb) list Derive 7 virtual void f() {cout<<"base::f"<
and ends at 0x400b52
.(7)#start执行程序,n单步执行(gdb) startTemporary breakpoint 1, main () at test.cpp:1919 cout<<"size of Base: "<
<
}(gdb) p vptr$6 = (long *) 0x400c90
(9) #查看函数表值,与之前查看函数地址一致(gdb) p (long*)vptr[0]$9 = (long *) 0x400ac8
(gdb) p (long*)vptr[1]$10 = (long *) 0x400b46
(gdb) p (long*)vptr[2]$11 = (long *) 0x400b1c

 

另vptr. vtable内存位置, refer  http://www.tuicool.com/articles/iUB3Ebi

转载地址:http://pvoul.baihongyu.com/

你可能感兴趣的文章
android开发(19) 调用手机的摄像头录像,并播放。
查看>>
SeekBar 事件响应和 自定义滑块图形
查看>>
Delphi XE5开发Android程序使用自定义字体文件.
查看>>
Python之while循环
查看>>
sqlserver的排序
查看>>
《Java从入门到精通》第四章学习笔记
查看>>
阿里云服务器Linux CentOS安装配置(零)目录
查看>>
SWTError: No more handles [gtk_init_check() failed] running platform tests (on Linux)
查看>>
kill、killall、pkill杀手三人组
查看>>
Deep learning:三十三(ICA模型)
查看>>
Oracle的导入导出
查看>>
maven下的pom.xml配置(SSM框架)
查看>>
js和jsp
查看>>
contentsize ,ios 7和 ios7之前的 有点差别,
查看>>
<cf>Sysadmin Bob
查看>>
比较与分析Groovy与Java
查看>>
3.11 返回数据到前一个Activity
查看>>
第33周星期二小结
查看>>
jquery 插件下载
查看>>
Dev的表格中编辑捕获EditValueChanged事件技巧
查看>>