标签:
其实这是我前一段时间思考过的一个问题,是在看《深入探索C++对象模型》这本书的时候我产生的一个疑问,最近在网上又看到类似的帖子,贴出来看看:
Answer 1:
我们都知道,虚函数是多态机制的基础,就是在程序在运行期根据调用的对象来判断具体调用哪个函数,现在我们来说说它的具体实现原理,主要说一下我自己的理解,如果有什么不对的地方请指正
在每个包含有虚函数的类的对象的最前面(是指这个对象对象内存布局的最前面,至于为什么是最前面,说来话长,这里就不说了,主要是考虑到效率问题)都有一个称之为虚函数指针(vptr)的东西指向虚函数表(vtbl),这个虚函数表(这里仅讨论最简单的单一继承的情况,若果是多重继承,可能存在多个虚函数表)里面存放了这个类里面所有虚函数的指针,当我们要调用里面的函数时通过查找这个虚函数表来找到对应的虚函数,这就是虚函数的实现原理。这里我假设大家都了解了,如果不了解可以去查下资料。好了,既然我们知道了虚函数的实现原理,虚函数指针vptr指向虚函数表vtbl,而且vptr又在对象的最前面,那么我们很容易可以得到虚函数表的地址,下面我写了一段代码测试了一下:
#include <iostream> #include <stdio.h> typedef void (*fun_pointer)(void); using namespace std; class Test { public: Test() { cout<<"Test()."<<endl; } virtual void print() { cout<<"Test::Virtual void print1()."<<endl; } virtual void print2() { cout<<"Test::virtual void print2()."<<endl; } }; class TestDrived:public Test { public: static int var; TestDrived() { cout<<"TestDrived()."<<endl; } virtual void print() { cout<<"TestDrived::virtual void print1()."<<endl; } virtual void print2() { cout<<"TestDrived::virtual void print2()."<<endl; } void GetVtblAddress() { cout<<"vtbl address:"<<(int*)this<<endl; } void GetFirstVtblFunctionAddress() { cout<<"First vbtl funtion address:"<<(int*)*(int*)this+0 << endl; } void GetSecondVtblFunctionAddress() { cout<<"Second vbtl funtion address:"<<(int*)*(int*)this+1 << endl; } void CallFirstVtblFunction() { fun = (fun_pointer)* ( (int*) *(int*)this+0 ); cout<<"CallFirstVbtlFunction:"<<endl; fun(); } void CallSecondVtblFunction() { fun = (fun_pointer)* ( (int*) *(int*)this+1 ); cout<<"CallSecondVbtlFunction:"<<endl; fun(); } private: fun_pointer fun; }; int TestDrived::var = 3; int main() { cout<<"sizeof(int):"<<sizeof(int)<<"sizeof(int*)"<<sizeof(int*)<<endl; fun_pointer fun = NULL; TestDrived a; a.GetVtblAddress(); cout<<"The var‘s address is:"<<&TestDrived::var<<endl; a.GetFirstVtblFunctionAddress(); a.GetSecondVtblFunctionAddress(); a.CallFirstVtblFunction(); a.CallSecondVtblFunction(); return 0; }
这里我们通过得到虚函数表的地址调用了里面的虚函数。
这几天又查了下资料,终于搞清楚虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata),这与微软的编译器将虚函数表存放在常量段存在一些差别。将上面的文件编译生成最终的可执行文件,然后利用命令:
objdump -s -x -d a.out | c++filt | grep "vtable" 可以得到以下输出
几个值得注意的问题
标签:
原文地址:http://www.cnblogs.com/laiqun/p/5887372.html