0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深度解析C++中的虚函数

jf_78858299 来源:阿Q正砖 作者:阿Q正砖 2023-02-15 11:14 次阅读

虚函数作为C++的重要特性,让人又爱又怕,爱它功能强大,但又怕驾驭不好,让它反咬一口,今天我们用CPU的角度,撕掉语法的伪装,重新认识一下虚函数。

虚函数是C++实现面向对象设计及多态特性的重要手段。没有虚函数,C++和C的区别就不大,都需要借助大量的“函数指针”,进行面向对象的程序设计(特别是功能扩展方面)。

有了虚函数的存在,函数指针的使用率大大降低,代码可读性,代码数量都能得到大幅度的改善。

最厉害的是,C++的虚函数实现机制,几乎同时在空间、效率上获得了最优解。

学习C++,虚函数是一条必经之路!

先来看两段简单代码

图片

让我们先比较一下普通函数体与虚函数体有什么区别,显然,两个函数是完全一致的,虚函数跟普通函数一样,都会夹带一个隐藏参数this指针。所以,如你所见,虚函数在实现方面,跟普通函数没有任何区别。

让我们再看看调用它们的时候,会有什么不同

图片

通过对比,大部分地方也是相同的,箭头指的那两条指令都是在输入:隐藏参数 this指针。唯一的区别是,调用普通函数时,call指令的目标地址在编译阶段就确定了,也就是所谓的“静态绑定”;但调用虚函数时,call指令只能根据rdx寄存器的值来确定函数的位置,也就是所谓的“动态绑定”。

再深入理解下这几条指令

图片

原来当类A有虚函数的时候,类A就会偷偷生成一个隐藏成员变量,方便起见,我们给这个隐藏变量起一个名字:V(指针类型),V存放着虚函数表的地址,根据偏移,就可以得到要执行的vtest_1 的地址,将其存在寄存器rdx里面,随后一条:call rdx 指令,一个虚函数的调用就完成了。如果说,类的成员函数会夹带隐藏参数 this指针,还能接受的话,那么,我说类还会夹带隐藏变量V,你能接受吗?如果真的存在隐藏变量V,在哪里给V初始化呢?答案是在A的构造函数中,把V初始化成类A的虚函数表地址,如下:

图片

尽管我没有写构造函数,编译器还是会给我 生成一个默认的构造函数 ,它一定、必须要帮我完成隐藏变量V的初始化。

当然,A有派生类B的话

图片

那么隐藏变量V会在B的构造函数中被初始化为B的虚函数表地址,从而保证A、B的虚函数相互独立,井水不犯河水,但考虑到派生类B的构造函数,还会调用基类A的构造函数。因此,变量V一会儿会被初始化成类A的虚函数表,一会又会被初始化成类B的虚函数表,为了避免晕头,往往会禁止在构造函数里面调用虚函数。

小结一下:

1、虚函数在函数体的实现方面跟普通函数没有任何区别。

2、虚函数的调用需要借助类对象的隐藏变量 V(vptr)来完成,隐藏变量V(vptr)会在构造函数中被初始化成虚函数表的内存地址。

3、调用任何虚函数的套路都是一样的,唯一的区别是要根据它们在虚函数表的位置设置正确的偏移量。

大家可以看看调用vtest_1()和调用vtest_2()的唯一区别是什么?

图片

不得不佩服虚函数的实现方法,几乎同时在效率的空间上得到了最优解,因为虚函数的出现,函数指针的使用率大大降低,如果你还是被函数指针困扰的时候,或许可以考虑一下虚函数。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10451

    浏览量

    206581
  • C++
    C++
    +关注

    关注

    21

    文章

    2066

    浏览量

    72901
  • 虚函数
    +关注

    关注

    0

    文章

    8

    浏览量

    1669
收藏 人收藏

    评论

    相关推荐

    C++软件工程师面试题

    1、 c++是面向对象的编程语言吗?C++函数(virtual) 是什么?有什么好处? (1)C++
    发表于 03-01 16:23

    C++标准编程:函数与内联

      曾经在讨论C++的时候,经常会问到:“函数能被声明为内联吗?”现在,我们几乎听不到这个问题了。现在听到的是:“你不应该使print成为内联的。声明一个
    发表于 05-03 11:53

    Zstack串口操作的深度解析(一)

    本帖最后由 eehome 于 2013-1-5 10:06 编辑 Zstack串口操作的深度解析(一)欢迎研究ZigBee的朋友和我交流。。。
    发表于 08-12 21:11

    I2C通信设计深度解析

    I2C通信设计深度解析
    发表于 08-12 21:31

    关于C++函数重载机制

    ,而且同类型的同名函数能够更好地发挥多种功能.宏观体现就是使用一个函数名字可以完成各种同类型但是不同细节的函数调用(例如,参数的类型不同,或者仅仅是多了一个控制量参数......).所以C++
    发表于 10-01 17:18

    关于C++函数是否必须重写

    说法1:网上说如果不是纯函数,子类可以不重写父类的函数。说法2:C++primer书上说的。所有的
    发表于 05-13 11:46

    关于C++函数指针的使用

    关于C++函数指针的使用(包含对typedef用法的讨论) (一)简单的函数指针的应用。 //形式1:返回类型(*函数名)(参数表)
    发表于 07-13 03:51

    如何完备地实现C++多态性?

    如何完备地实现C++多态性?函数怎么使用?
    发表于 04-28 06:44

    常用的C/C++接口函数有哪些

    ,sqlite3可以直接通过shell运行,不过这个也只限于测试使用,在实际的项目编程,我们还是要使用sqlite3提供的C/C++接口函数,也就是API接口,常用的接口
    发表于 11-04 08:43

    C++的四种类型转换分别是哪些?C++析构函数的作用是什么

    C++的四种类型转换分别是哪些?C++析构函数的作用是什么?在C语言中关键字static主要
    发表于 12-24 06:57

    请问c++的beep函数是什么意思?

    c++的beep函数是什么意思?
    发表于 02-28 07:41

    C语言深度解析

    C语言深度解析,本资料来源于网络,对C语言的学习有很大的帮助,有着较为深刻的解析,可能会对读者有一定的帮助。
    发表于 09-28 07:00

    什么是C++函数? 应该怎么定义? 用途是什么?

    什么是C++函数? 应该怎么定义? 主要用途是什么?
    发表于 11-08 06:58

    如何深度解析C++拷贝构造函数详细资料说明

    本文档的主要内容详细介绍的是如何深度解析C++拷贝构造函数详细资料说明。
    发表于 07-05 17:41 0次下载
    如何<b class='flag-5'>深度</b><b class='flag-5'>解析</b><b class='flag-5'>C++</b>拷贝构造<b class='flag-5'>函数</b>详细资料说明

    如何在中断C函数中调用C++

    之前,我们在单片机程序开发时都会面对中断函数。众所周知的,这个中断函数肯定是要用C函数来定义的。我在用C++进行程序开发的时候就发现了一个需要解决了问题:在断
    发表于 05-09 18:17 0次下载
    如何在中断C<b class='flag-5'>函数</b>中调用<b class='flag-5'>C++</b>