b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

【每日一题】虚函数的实现原理(二)

电脑杂谈  发布时间:2021-05-25 10:02:27  来源:网络整理

我们都知道虚函数多态机制的基础,即在程序运行时根据被调用对象确定要调用哪个函数。现在,让我们讨论一下它的具体实现原理,主要是关于我自己的理解,如果有什么问题请纠正我

位于包含虚函数的类的每个对象的最前端(指此对象对象的内存布局的最前端。至于为什么它是最前端,这是一个很长的故事,所以我不再赘述在这里,主要考虑(效率问题)有一个称为虚拟函数指针(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

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...