虚函数作为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()的唯一区别是什么?
不得不佩服虚函数的实现方法,几乎同时在效率的空间上得到了最优解,因为虚函数的出现,函数指针的使用率大大降低,如果你还是被函数指针困扰的时候,或许可以考虑一下虚函数。
关于导电胶点胶加工的应用和区别分析
苹果公司将对iPhone等设备的运行系统进行iOS 13更新
Plexus推出售价低廉的VR触觉手套
TI旋变接口为工业驱动和新能源汽车提供集成优势
骁龙870 5G移动平台性能评测
深度解析C++中的虚函数
逆变器在风力发电系统与电网接口上的作用
智能交通监控系统可以准确地识别道路事故
介绍一篇基于DMAMUX同步事件的应用演示
GIF摄影棚的制作教程
IC行业的现状如何,IC行业有何发展趋势
AI武器会怎样改变战争的形式
25美元用3D打印机改造谷歌眼镜,用眼睛玩《超级玛丽》!
新型3D打印机有助于提高大型零件的生产率
RADIO ENERGIE雷恩防爆式编码器
基于ADS7843控制芯片和单片机实现应用系统的连接与设计
紧急切断阀安装规范_紧急切断阀安装注意事项
【科普】移动无线通信技术技术1G到5G之路
十倍电压放大器电路图大全(前置放大/电压跟随器/LM386音响功放电路)
2790数字源表开关系统的主要特点及应用优势