这篇文章主要介绍c++虚函数在g++中的实现方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
探索c++虚函数在g++中的实现
在开始之前,原谅我先借用一张图黑一下c++:
"无敌”的c++
如果你也在写c++,请一定小心…至少,你要先有所了解:<强>当你在写虚函数的时候,g++在写什么? 强>
先写个例子
为了探索c++虚函数的实现,我们首先编写几个用来测试的类,代码如下:c++
# include & lt; iostream> 使用名称空间性病; 类Base1 { 公众: 虚拟空间f () { cout & lt; & lt;“Base1: f (),& lt; & lt;endl; } }; 类Base2 { 公众: 虚拟空间g () { cout & lt; & lt;“Base2: g ()“;& lt; & lt;endl; } }; 派生类:公共Base1公共Base2 { 公众: 虚拟空间f () { cout & lt; & lt;“派生:f (),& lt; & lt;endl; } 虚拟空间g () { cout & lt; & lt;“派生:g ()“;& lt; & lt;endl; } 虚拟空间h () { cout & lt; & lt;“派生:h ()“;& lt; & lt;endl; } }; int主要(int命令行参数个数,char * argv []) { 派生的ins; Base1, b1=ins; Base2, b2=ins; 派生和d=ins; b1.f (); b2.g (); d.f (); d.g (); d.h (); }
代码采用了多继承,是为了更多的分析出g++的实现本质,用UML简单的画一下继承关系:
示例代码UML图
代码的输出结果和预期的一致,c++实现了虚函数覆盖功能,代码输出如下:
派生::f () 派生:g () 派生:f () 派生:g () 派生:h ()
开始分析!
我写这篇文章的重点是尝试解释g++编译在底层是如何实现虚函数覆盖和动态绑定的,因此我假定你已经明白基本的虚函数概念以及虚函数表(vtbl)和虚函数表指针(vptr)的概念和在继承实现中所承担的作用,如果你还不清楚这些概念,建议你在继续阅读下面的分析前先补习一下相关知识,陈皓的《c++虚函数表解析》系列是一个不错的选择。
通过本文,我将尝试解答下面这三个问题:
- <李>
g++如何实现虚函数的动态绑定?
李> <李>vtbl在何时被创建? vptr又是在何时被初始化?
李> <李>在Linux中运行的c++程序虚拟存储器中,vptr, vtbl存放在虚拟存储的什么位置?
李>首先是第一个问题:
g++如何实现虚函数的动态绑定?
这个问题乍看简单,大家都知道是通过vptr和vtbl实现的,那就让我们刨根问底的看一看,g++是如何利用vptr和vtbl实现的。
第一步,使用-fdump-class-hierarchy参数导出g++生成的类内存结构:
Vtable Base1 Base1:: _ZTV5Base1: 3 u条目 0 (int (*) (…)) 0 4 (int (*) (…)),_ZTI5Base1) 8 Base1: f 类Base1 大?4=4一致 基地大?4基地对齐=4 Base1 (0 xb6acb438) 0几乎空无一人 vptr=((,Base1: _ZTV5Base1) + 8 u) 的Vtable Base2 Base2:: _ZTV5Base2: 3 u条目 0 (int (*) (…)) 0 4 (int (*) (…)),_ZTI5Base2) 8 Base2: g 类Base2 大?4=4一致 基地大?4基地对齐=4 Base2 (0 xb6acb474) 0几乎空无一人 vptr=((,Base2: _ZTV5Base2) + 8 u) Vtable为派生 派生::_ZTV7Derived: 8 u条目 0 (int (*) (…)) 0 4 (int (*) (…)),_ZTI7Derived) 8派生:f 12派生:g 16派生:h 20 (int (*) (…)) 0 x000000004 24 (int (*) (…)),_ZTI7Derived) 28派生::_ZThn4_N7Derived1gEv 类的派生 大?8=4一致 基础尺寸一致=4=8基地 派生(0 xb6b12780) 0 vptr=((,派生:_ZTV7Derived) + 8 u) Base1 (0 xb6acb4b0) 0几乎空无一人 初选,派生(0 xb6b12780) Base2 (0 xb6acb4ec) 4几乎空无一人 vptr=((,派生:_ZTV7Derived) + 28 u)
如果看不明白这些乱七八糟的输出,没关系(当然能看懂更好),把上面的输出转换成图的形式就清楚了:
vptr和vtbl
其中有几点尤其值得注意:
- <李>
我用来测试的机器是32位机,所有vptr占4个字节,每个vtbl中的函数指针也是4个字节