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

    文章

    10855

    浏览量

    211605
  • C++
    C++
    +关注

    关注

    22

    文章

    2108

    浏览量

    73624
  • 虚函数
    +关注

    关注

    0

    文章

    8

    浏览量

    1698
收藏 人收藏

    评论

    相关推荐

    C++函数virtual详解

    C++ 函数用于解决动态多态问题,函数的作用是允许在派生类
    发表于 09-07 11:27 2879次阅读

    C++函数操作指南(含代码示例)

    本期博客,我们来介绍C++函数,并给出一些实际操作的建议。
    发表于 02-12 10:12 673次阅读

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

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

    关于C++函数重载机制

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

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

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

    C++如何处理内联函数

    当一个函数是内联和函数时,会发生代码替换或使用表调用吗? 为了弄 清楚内联和函数,让我们将
    发表于 11-29 11:59 28次下载

    C++程序设计教程之多态性与函数的详细资料说明

    本文档详细介绍的是C++程序设计教程之多态性与函数的详细资料说明主要资料包括了:1 多态性的概念,2 一个典型的例子,3 函数,4 纯
    发表于 03-14 16:39 5次下载
    <b class='flag-5'>C++</b>程序设计教程之多态性与<b class='flag-5'>虚</b><b class='flag-5'>函数</b>的详细资料说明

    如何深度解析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次下载
    如何在中断<b class='flag-5'>C</b><b class='flag-5'>函数</b><b class='flag-5'>中</b>调用<b class='flag-5'>C++</b>

    图文详解:C++表的剖析

    图文详解:C++表的剖析
    的头像 发表于 06-29 14:23 2534次阅读
    图文详解:<b class='flag-5'>C++</b><b class='flag-5'>虚</b>表的剖析

    EE-128:C++的DSP:从C++调用汇编类成员函数

    EE-128:C++的DSP:从C++调用汇编类成员函数
    发表于 04-16 17:04 2次下载
    EE-128:<b class='flag-5'>C++</b><b class='flag-5'>中</b>的DSP:从<b class='flag-5'>C++</b>调用汇编类成员<b class='flag-5'>函数</b>

    C++语法的inline内联函数详解

    上节我们分析了C++基础语法的const,static以及 this 指针,那么这节内容我们来看一下 inline 内联函数吧! inline 内联函数 特征 相当于把内联函数里面的内
    的头像 发表于 09-09 09:33 3360次阅读

    C++如何用函数实现多态

    01 — C++函数探索 C++是一门面向对象语言,在C++里运行时多态是由
    的头像 发表于 09-29 14:18 1694次阅读

    一文详解函数及其相关知识点

    本期是C++基础语法分享的第七节,今天给大家来分享一下: (1)析构函数; (2)纯函数; (3)
    的头像 发表于 10-13 10:14 7925次阅读

    函数C++开发者如何有效利用

    函数是基类声明的成员函数,且使用者期望在派生类中将其重新定义。那么,在 C++ ,什么是
    的头像 发表于 02-11 09:39 934次阅读