我们都知道虚函数是多态机制的基础,即在程序运行时根据被调用对象确定要调用哪个函数。现在,让我们讨论一下它的具体实现原理,主要是关于我自己的理解,如果有什么问题请纠正我
位于包含虚函数的类的每个对象的最前端(指此对象对象的内存布局的最前端。至于为什么它是最前端,这是一个很长的故事,所以我不再赘述在这里,主要考虑(效率问题)有一个称为虚拟函数指针(vptr)的东西指向虚拟函数表(vtbl)。该虚拟函数表(这里仅讨论单继承的最简单情况)。多重继承,可能会有更多的虚函数表)存储此类中所有虚函数的指针。当我们要在内部调用函数时,可以通过查找虚函数表来找到对应的虚函数。虚拟函数的实现原理。这里我假设每个人都了解,如果不了解,就可以检查信息。好了,现在我们知道了虚拟函数的实现原理,虚拟函数指针vpt r指向虚拟函数表vtbl,而vptr在对象的顶部,那么我们可以很容易地获得虚拟函数表的地址,下面我写了一段代码对其进行了测试:
#include#include 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)的只读数据部分中,这是相同的作为存储虚拟函数表的Microsoft编译器,常量部分存在一些差异。编译上面的文件以生成最终的可执行文件,然后使用以下命令:
objdump -s -x -d a.out | c ++过滤器| grep“ vtable”您可以获得以下输出
从上面已经很清楚,这两个类Test和TestDrived存储在.rodata中,至于上面的命令,可以稍作解释,可以读取objdump
可执行文件中的详细信息,包括可执行文件的标题,节,符号等。 objdump获得的可执行文件的许多符号都是
我们不理解,或者与我们源代码中的函数或变量不同。这是因为C ++支持函数重载,而C ++会对所有符号进行重载。
装饰,很多材料将其称为类似于“功能签名”或“符号装饰”的概念,但是我们需要在源代码中将其转换为符号,以供使用
c ++ filt命令完成了,好了,到此结束。简而言之,此处介绍了有关虚拟功能表的具体细节。
几个值得注意的问题
虚拟功能表是特定于类的,也就是说,它是针对一个类的。它有点像类中的staic成员变量,也就是说,它属于类的所有对象,不是特定于某个对象的,是类的所有对象所共有的。虚函数表由编译器实现。不同类型的编译器可以以不同方式实现。如前所述,vptr位于对象的顶部,但是还有其他方法可以实现它。但是,当前由gcc和Microsoft编译。vptr始终放在对象内存布局的最前面。尽管我们知道vptr指向虚拟功能表,但存储在内存中的虚拟功能表在哪里,尽管我们已经可以在此处获取虚拟功能表的地址。实际上,在执行构造函数时会初始化虚拟函数指针,并将虚拟函数表存储在可执行文件中。以下博客测试了Microsoft的编译器将虚拟功能表存储在目标文件或可执行文件的常量部分中,但是我没有在gcc下的汇编文件中找到vtbl的特定存储位置。主要原因是尚未深入了解可执行文件的加载和运行原理。我相信,掌握了这些知识之后,很快就会发现虚拟功能表存储在目标文件的哪个段中。经过测试,在gcc编译器的实现中,虚拟功能表vtable存储在读取的文件中可执行文件的纯数据段.rodata。
参考文献:
1.
2.深入探索c ++对象模型
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-377966-1.html
婚房也是现成的
我官网买的米2差不多充满两次是可以的