C++中虚函数的作用和虚函数的工作原理
1 C++中虚函数的作用和多态
虚函数: 实现类的多态性
关键字:虚函数;虚函数的作用;多态性;多态公有继承;动态联编
C++中的虚函数的作用主要是实现了多态的机制。基类定义虚函数,子类可以重写该函数;在派生类中对基类定义的虚函数进行重写时,需要在派生类中声明该方法为虚方法。
当子类重新定义了父类的虚函数后,当父类的指针指向子类对象的地址时,[即B b; A a = &b;] 父类指针根据赋给它的不同子类指针,动态的调用子类的该函数,而不是父类的函数(如果不使用virtual方法,请看后面★*),且这样的函数调用发生在运行阶段,而不是发生在编译阶段,称为动态联编。而函数的重载可以认为是多态,只不过是静态的。注意,非虚函数静态联编,效率要比虚函数高,但是不具备动态联编能力。
★如果使用了virtual关键字,程序将根据引用或指针指向的 对 象 类 型 来选择方法,否则使用引用类型或指针类型来选择方法。
下面的例子解释动态联编性:
class A{
private:
int i;
public:
A();
A(int num) :i(num) {};
virtual void fun1();
virtual void fun2();
};
class B : public A{
private:
int j;
public:
B(int num) :j(num){};
virtual void fun2();// 重写了基类的方法
};
// 为方便解释思想,省略很多代码
A a();
B b();
A *a1_ptr = &a;
A *a2_ptr = &b;
// 当派生类“重写”了基类的虚方法,调用该方法时
// 程序根据 指针或引用 指向的 “对象的类型”来选择使用哪个方法
a1_ptr->fun2();// call A::fun2();
a2_ptr->fun2();// call B::fun1();
// 否则
// 程序根据“指针或引用的类型”来选择使用哪个方法
a1_ptr->fun1();// call A::fun1();
a2_ptr->fun1();// call A::fun1();
2. 虚函数的底层实现机制
实现原理:虚函数表+虚表指针
关键字:虚函数底层实现机制;虚函数表;虚表指针
编译器处理虚函数的方法是:为每个类对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,称为虚表指针(vptr),这种数组成为虚函数表(virtual function table, vtbl),即,每个类使用一个虚函数表,每个类对象用一个虚表指针。
举个例子:基类对象包含一个虚表指针,指向基类中所有虚函数的地址表。派生类对象也将包含一个虚表指针,指向派生类虚函数表。看下面两种情况:
如果派生类重写了基类的虚方法,该派生类虚函数表将保存重写的虚函数的地址,而不是基类的虚函数地址。
如果基类中的虚方法没有在派生类中重写,那么派生类将继承基类中的虚方法,而且派生类中虚函数表将保存基类中未被重写的虚函数的地址。注意,如果派生类中定义了新的虚方法,则该虚函数的地址也将被添加到派生类虚函数表中。
下面的图片体现了上述的底层实现机制:

C++primer第六版第十三章的虚函数的工作原理
编译器处理虚函数的方法是:
给每个对象添加一个指针,存放了指向虚函数表的地址,虚函数表存储了为类对象进行声明的虚函数地址。比如基类对象包含一个指针,该指针指向基类所有虚函数的地址表,派生类对象将包含一个指向独立地址表的指针,如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址,如果派生类没有重新定义虚函数,该虚函数表将保存函数原始版本的地址。如果派生类定义了新的虚函数,则该函数的地址将被添加到虚函数表中,注意虚函数无论多少个都只需要在对象中添加一个虚函数表的地址。

调用虚函数时,程序将查看存储在对象中的虚函数表地址,转向相应的虚函数表,使用类声明中定义的第几个虚函数,程序就使用数组的第几个函数地址,并执行该函数。
使用虚函数后的变化:
(1) 对象将增加一个存储地址的空间(32位系统为4字节,64位为8字节)。
(2) 每个类编译器都创建一个虚函数地址表
(3) 对每个函数调用都需要增加在表中查找地址的操作。
虚函数的注意事项
总结前面的内容
(1) 基类方法中声明了方法为虚后,该方法在基类派生类中是虚的。
(2) 若使用指向对象的引用或指针调用虚方法,程序将根据对象类型来调用方法,而不是指针的类型。
(3)如果定义的类被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚。
构造函数不能为虚函数。
基类的析构函数应该为虚函数。
友元函数不能为虚,因为友元函数不是类成员,只有类成员才能是虚函数。
如果派生类没有重定义函数,则会使用基类版本。
重新定义继承的方法若和基类的方法不同(协变除外),会将基类方法隐藏;如果基类声明方法被重载,则派生类也需要对重载的方法重新定义,否则调用的还是基类的方法。
参考:
https://blog.csdn.net/iFuMI/article/details/51088091
https://blog.csdn.net/HuYingJie_1995/article/details/88085213
C++中虚函数的作用和虚函数的工作原理的更多相关文章
- 深入理解JS中的对象(三):class 的工作原理
目录 序言 class 是一个特殊的函数 class 的工作原理 class 继承的原型链关系 参考 1.序言 ECMAScript 2015(ES6) 中引入的 JavaScript 类实质上是 J ...
- 深入理解JS中的对象(二):new 的工作原理
目录 序言 不同返回值的构造函数 深入 new 调用函数原理 总结 参考 1.序言 在 深入理解JS中的对象(一):原型.原型链和构造函数 中,我们分析了JS中是否一切皆对象以及对象的原型.原型链和构 ...
- struts2中struts.xml和web.xml文件解析及工作原理
web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp ...
- C++中虚函数的作用浅析
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- C++中虚函数的作用是什么?它应该怎么用呢?(转)
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- C++中虚函数的作用
一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你就应该从这里开始) 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Po ...
- C++中虚函数的作用是什么?它应该怎么用呢?
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- C++之虚函数的作用和使用方法
在同一类中是不能定义两个名字相同.参数个数和类型都相同的函数的,否则就是“重复定义”.但是在类的继承层次结构中,在不同的层次中可以出现名字相同.参数个数和类型都相同而功能不同的函数.例如在例12.1( ...
- c++虚函数的作用是什么?
<深入浅出MFC>中形容虚函数是执行一般化操作,一直没有领悟要点.现在的体悟是抽象,先前考虑问题都是由抽象到具象,比如下文中的示例,由上(虚基类的「怪物」)至下(派生类的三个子类「狼」「蜘 ...
随机推荐
- 利用sklearn的Pipeline简化建模过程
很多框架都会提供一种Pipeline的机制,通过封装一系列操作的流程,调用时按计划执行即可.比如netty中有ChannelPipeline,TensorFlow的计算图也是如此. 下面简要介绍skl ...
- 【并行计算-CUDA开发】显卡两大生产商
ATI显卡 ATI显卡即AMD显卡.俗称A卡.搭载AMD公司出品的显示芯片.与NVIDIA齐名,同为世界两大显示芯片厂商. 不同的是AMD不是只有显卡,而且还出品CPU(处理器),其AMD处理器与In ...
- vsphere6.7+Horizon7.8推送桌面遇到的一些问题
系统环境 esxi6.7+vSphere6+Horizon7.8 VC环境为windows server 2016 standard 问题描述 vc安装 链接外部数据库找不到DSN 安装view co ...
- 【Python】【demo实验7】【练习实例】【完全平方数相关】
题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少? 程序分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去 掉不满足条件的排列. ...
- oracle——学习之路(oracle内置函数)
oracle与很多内置函数,主要分为单行函数与集合函数. 首先要提一下dual表,它oracle的一个表,没有什么实质的东西,不能删除它,否则会造成Oracle无法启动等问题,他有很大用处,可以利用它 ...
- drf-更新四大接口-单改整体-单改局部-群改整体-群改局部-04
目录 复习 基于前一天序列化基础 整体单改 单与整体局部修改 复习 """ 1.ModelSerializer序列化类 models.py class BaseModel ...
- X86逆向2:提取按钮通杀特征码
本章我们将学习特征码的提取与定位,特征码是软件中一段固定的具有标志性的代码片段,特征码的用途非常广泛,最常见的就是杀毒软件的查杀了,查杀就是根据特征码定位技术实现的,再比如木马的免杀也是修改了特征码的 ...
- Active Learning 主动学习
Active Learning 主动学习 2015年09月30日 14:49:29 qrlhl 阅读数 21374 文章标签: 算法机器学习 更多 分类专栏: 机器学习 版权声明:本文为博主原创文 ...
- Graph、DFS、BFS
Graph.java package Graph; import LinearLIst.bag.Bag; import edu.princeton.cs.algs4.In; public class ...
- 解决github pages和github .md文件图片不显示
博客园上传的图片,在github上无法显示. 在github项目下建立img文件夹,放上图片 两种方式 项目绝对路径 https://raw.githubusercontent.com/用户名/项目名 ...