文章内容转自关于C++中虚函数表存放位置的思考 作者:fuliangcheng
1、虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata)
2、微软的编译器将虚函数表存放在常量段存在一些差别
虚函数是多态机制的基础,就是在程序在运行期根据调用的对象来判断具体调用哪个函数,实现原理:
在每个包含有虚函数的类的对象的最前面(是指这个对象对象内存布局的最前面,至于为什么是最前面,说来话长,这里就不说了,主要是考虑到效率问题)都有一个称之为虚函数指针(vptr)的东西指向虚函数表(vtbl),这个虚函数表(这里仅讨论最简单的单一继承的情况,若果是多重继承,可能存在多个虚函数表)里面存放了这个类里面所有虚函数的指针,当我们要调用里面的函数时通过查找这个虚函数表来找到对应的虚函数,这就是虚函数的实现原理。这里我假设大家都了解了,如果不了解可以去查下资料。好了,既然我们知道了虚函数的实现原理,虚函数指针vptr指向虚函数表vtbl,而且vptr又在对象的最前面,那么我们很容易可以得到虚函数表的地址
虚继承的提出就是为了解决多重继承时,可能会保存两份副本的问题,也就是说用了虚继承就只保留了一份副本,但是这个副本是被多重继承的基类所共享的,该怎么实现这个机制呢?编译器会在派生类B的实例中保存一个A的实例,然后在B中加入一个变量,这个变量是A的实例在实际B实例中的偏移量,实际上B中并不直接保存offset的值,而是保存的一个指针,这个指针指向一个表vbtable,vbtable表中保存着所有虚继承的基类在实例中的offset值,多个B的实例共享这个表,每个实例有个单独的指针指向这个表
几个值得注意的问题
- 虚函数表是class specific的,也就是针对一个类来说的,这里有点像一个类里面的staic成员变量,即它是属于一个类所有对象的,不是属于某一个对象特有的,是一个类所有对象共有的。
- 虚函数表是编译器来选择实现的,编译器的种类不同,可能实现方式不一样,就像前面我们说的vptr在一个对象的最前面,但是也有其他实现方式,不过目前gcc 和微软的编译器都是将vptr放在对象内存布局的最前面。
- 虽然我们知道vptr指向虚函数表,那么虚函数表具体存放在内存哪个位置呢,虽然这里我们已经可以得到虚函数表的地址。实际上虚函数指针是在构造函数执行时初始化的,而虚函数表是存放在可执行文件中的。下面的一篇博客测试了微软的编译器将虚函数表存放在了目标文件或者可执行文件的常量段中,blog.csdn.net/vicness/article/details/3962767,不过我在gcc下的汇编文件中没有找到vtbl的具体存放位置,主要是对可执行文件的装载和运行原理还没有深刻的理解,相信不久有了这些知识之后会很轻松的找到虚函数表到底存放在目标文件的哪一个段中。
- 经过测试,在gcc编译器的实现中虚函数表vtable存放在可执行文件的只读数据段.rodata中。
网友评论