首页 > 其他分享 >虚函数与虚表浅分析

虚函数与虚表浅分析

时间:2022-08-22 09:56:04浏览次数:87  
标签:分析 虚表 函数 基类 virtual 地址 派生类 public

虚函数以及虚函数表的特征:

1.虚函数表是全局共享的元素,即全局仅有一个.
2.虚函数表类似一个数组,类对象中存储 vptr 指针,指向虚函数表.即虚函数表不是函数,不是程序代码,不肯能存储在代码段.
3.虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表,所以不再堆中.

根据以上特征,虚函数表类似于类中静态成员变量.静态成员变量也是全局共享,大小确定.

c/c++程序所占用的内存一共分为五种:

栈区,堆区,程序代码区,全局数据区(静态区),文字常量区.

所以个人推测虚函数表和静态成员变量一样,存放在全局数据区.

虚函数与虚表几个值得注意的问题:

1.虚函数表是class specific的,也就是针对一个类来说的,这里有点像一个类里面的staic成员变量,即它是属于一个类所有对象的,不是属于某一个对象特有的,是一个类所有对象共有的。

2.虚函数表是编译器来选择实现的,编译器的种类不同,可能实现方式不一样,就像前面我们说的vptr在一个对象的最前面,但是也有其他实现方式,不过目前gcc 和微软的编译器都是将vptr放在对象内存布局的最前面。

3.虽然我们知道 vptr 指向虚函数表,那么虚函数表具体存放在内存哪个位置呢,虽然这里我们已经可以得到虚函数表的地址。实际上虚函数指针是在构造函数执行时初始化的,而虚函数表是存放在可执行文件中的。

4.有一篇博客测试了微软的编译器将虚函数表存放在了目标文件或者可执行文件的常量段中,不过我在gcc下的汇编文件中没有找到 vtbl 的具体存放位置,主要是对可执行文件的装载和运行原理还没有深刻的理解,相信不久有了这些知识之后会很轻松的找到虚函数表到底存放在目标文件的哪一个段中。经过测试,在gcc编译器的实现中虚函数表vtable存放在可执行文件ELF的只读数据段.rodata中。

分析下面的一段代码,可以得出虚拟继承的特点


    class MyClass
    {
        int var;
    public:
        virtual void fun()
        {}
    };

    class MyClassA:public MyClass
    {
        int varA;
    public:
        virtual void fun()
        {}
        virtual void funA()
        {}
    };

    class MyClassB:public MyClass
    {
        int varB;
    public:
        virtual void fun()
        {}
        virtual void funB()
        {}
    };
    class MyClassC:public MyClassA,public MyClassB
    {
        int varC;
    public:
        virtual void funB()
        {}
    virtual void funC()
        {}
    };

    class MyClassA:virtual public MyClass // 虚继承
    class MyClassB:virtual public MyClass // 虚继承
    class MyClassC:public MyClassA,public MyClassB

虚拟继承是为了解决多重继承下公共基类的多份拷贝问题。因此, 虚基类表每项记录了被继承的虚基类子对象相对于虚基类表指针的偏移量。

比如 MyClassA 的虚基类表第二项记录值为24,正是MyClass::vfptr相对于MyClassA::vbptr的偏移量,同理MyClassB的虚基类表第二项记录值12也正是MyClass:: vfptr 相对于MyClassB::vbptr的偏移量。

虚函数的底层实现机制

编译器处理虚函数的方法是:为每个类对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,称为虚表指针(vptr)这种数组成为虚函数表(virtual function table, vtbl),即,每个类使用一个虚函数表,每个类对象用一个虚表指针。

举个例子:基类对象包含一个虚表指针,指向基类中所有虚函数的地址表。派生类对象也将包含一个虚表指针,指向派生类虚函数表。

看下面两种情况:

如果派生类重写了基类的虚方法,该派生类虚函数表将保存重写的虚函数的地址,而不是基类的虚函数地址。

如果基类中的虚方法没有在派生类中重写,那么派生类将继承基类中的虚方法,而且派生类中虚函数表将保存基类中未被重写的虚函数的地址。注意,如果派生类中定义了新的虚方法,则该虚函数的地址也将被添加到派生类虚函数表中。

编译器处理虚函数的方法是:

给每个对象添加一个指针,存放了指向虚函数表的地址,虚函数表存储了为类对象进行声明的虚函数地址。比如基类对象包含一个指针,该指针指向基类所有虚函数的地址表,派生类对象将包含一个指向独立地址表的指针,如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址,如果派生类没有重新定义虚函数,该虚函数表将保存函数原始版本的地址。如果派生类定义了新的虚函数,则该函数的地址将被添加到虚函数表中,注意虚函数无论多少个都只需要在对象中添加一个虚函数表的地址。

使用虚函数后的变化:
(1) 对象将增加一个存储地址的空间(32位系统为4字节,64位为8字节)。
(2) 针对每个类,编译器都创建一个虚函数地址表
(3) 对每个函数调用都需要增加在表中查找地址的操作。

标签:分析,虚表,函数,基类,virtual,地址,派生类,public
From: https://www.cnblogs.com/success-zh/p/16611825.html

相关文章

  • 系统分析与设计方法---需求分析与软件设计
      需求分析是软件生命周期中相当重要的一个阶段。根据 StandishGroup 对 23000 个项目进行的研究结果表明,28%的项目彻底失败,46%的项目超出经费预算或者超出工......
  • JQuery事件绑定&入门函数&样式控制、JQuery_选择器_基本选择器
    JQuery事件绑定&入门函数&样式控制选择器:筛选具有相似的特征的元素(标签)基本语法学习:1事件的绑定2入口函数3样式控制window.on......
  • 哈希函数的构造方法
    https://www.cnblogs.com/gj-Acit/archive/2013/05/06/3062628.html哈希函数的构造方法 哈希函数的构造方法本文阐述了哈希函数的构造方法有很多,但应注意两个原则:......
  • JS compose 函数实现
    functioncompose(...funcs){letlength=funcs.length;if(length===0){return(arg)=>arg;}if(length===1){return......
  • python: 绘制数学函数
    1importmatplotlib.pyplotasplt2importnumpyasnp34#100linearlyspacednumbers5x=np.linspace(-5,5,100)67#thefunction,whichisy=......
  • mysql6/视图/触发器/事务/四种隔离级别/事务日志/mvcc/内置函数/存储过程/索引/索引的
    视图触发器事务事务处理四种隔离级别事务日志MVCC内置函数存储过程索引索引的意义慢查询优化查询索引模拟视图1.什么是视图?视图是类似于临时表,由sql......
  • C++中函数指针使用
    类成员函数指针(memberfunctionpointer),是C++语言的一类指针数据类型,用于存储一个指定类具有给定的形参列表与返回值类型的成员函数的访问信息。一般我们是不会使用的,都......
  • 常用函数
    MySQL函数官网:https://dev.mysql.com/doc/refman/5.7/en/常用函数--==============常用函数================--数学运算SELECTABS(-8)SELECTCEILING(9.4)......
  • 2022-08-18 MySQL常用函数
    MySQL常用函数聚合函数count:计数。count(*)≈count(1)>count(主键)count(*):MySQL对count(*)底层优化,count(0)。count(1)count(主键)count(字段)min:最小值max:最......
  • 2022.8.21 四大函数式接口与Stream流式计算
    12、四大函数式接口(重点)   函数接口:只有一个方法的接口    @FunctionalInterface publicinterfaceRunnable{     publicabstractvoidrun(......