虚函数是什么?虚函数是怎么实现的?什么时候用虚函数?

news2024/11/15 15:49:57

发现自己的笔记记录得四处都是,这些天统一整理汇总一下


1. 虚函数和多态的关系:

虚函数 往往 用于 实现 C++的多态性 

2.  什么是多态:

(1)Wiki定义 : 指计算机程序运行时,相同的消息 发送给 多个不同的类别对象,系统可依据对象所属类别,引发 该对应类别的方法,而有不同的行为。

简单来说,多态 就是指   相同的消息  给予  不同的对象 会引发 不同的动作

常见的使用场景 :父类指针 指向 子类实例,调用函数时,实际调用的是 指针指向的实际类型,即子类 的成员函数 。

多态性 使得程序只有 在 运行时 才能动态确定 到底是执行哪个函数,

            而 不是在编译时静态确定的

3. 什么是 虚函数:

虚函数则是加了 virtual 修饰词的类的成员函数


4. 一个 虚函数 和普通函数使用的比较:

 可以总结为:“当使用类的指针调用成员函数时,

                       普通函数指针类型 决定,

                       虚函数 由  指针指向的实际类型 决定”。

[上述代码解释]:在上述例子中,我们首先定义了一个基类base,基类有一个名为vir_func的虚函数,和一个名为func的普通成员函数。而类A,B都是由类base派生的子类,并且都对成员函数进行了重载。然后我们定义三个base*类型的指针Base、a、b分别指向类base、A、B。可以看到,当使用这三个指针调用func函数时,调用的都是基类base的函数。而使用这三个指针调用虚函数vir_func时,调用的是指针指向的实际类型的函数。最后,我们将指针b做强制类型转换,转换为A*类型,然后分别调用func和vir_func函数,发现普通函数调用的是类A的函数,而虚函数调用的是类B的函数。以上,我们可以得出结论“当使用类的指针调用成员函数时,普通函数由指针类型决定,而虚函数由指针指向的实际类型决定”。   


5. 虚函数是怎么实现的:

对于一个 只包含非静态成员变量和普通成员函数的类,

如“class C {void fun_a();void fun_b();int var};”,   其内存布局如下图:

 其中 成员函数放在代码区,为该类的所有对象公有,即不管新建多少个该类的对象,所对应的都是同一个函数存储区的函数

而成员变量则为各个对象所私有,即每新建一个对象都会新建一块内存区用来存储var值。

在调用成员函数时,程序会根据类的类型,找到对应代码区所对应的函数并进行调用。

在文章开头的例子中,Base、a、b都是base类型的指针。调用普通函数时,程序根据指针的类型到类base所对应的代码区找到所对应的函数,所以都调用了类base的func函数,即指针的类型决定了普通函数的调用。

==》 对于虚函数,又是怎么实现的呢?

对于“class D{void func_a(); virtul void func_b(); int var}” 这个类,其内存布局为:

这时如果sizeof一个类D的对象,会发现比类C的对象大4个字节。多出来的这4个字节就是实现虚函数的关键----虚函数表指针vptr

这个指针指向一张名为 “虚函数表”(vtbl)的表,而表中的数据则为函数指针,存储了虚函数fun_b()具体实现所对应的位置。

注意,普通函数、虚函数、虚函数表都是同一个类的所有对象公有的

           只有 成员变量 和 虚函数表指针vptr 是每个对象私有的,sizeof的值也只包括vptr和var所占内存的大小(也是个常出现的问题),并且vptr通常会在对象内存的最起始位置。

        另外,当类有多个虚函数时,仍然只有一个虚函数表指针vptr,而此时的虚函数表vtbl中会有多个函数指针,分别指向对应的虚函数实现区域。 

类的各个实例 共享 类 的 虚函数表,但,每个实例 都有一个属于自己的成员,vptr 指向 该类的虚函数表 【且,虚表指针是对象的第一个数据成员】

》一个非常清晰的文章:明明白白——虚函数,虚指针,虚表,虚继承 - 简书 (jianshu.com)


一些常见 虚函数 相关 问题:

1. 构造函数和析构函数可以是虚函数吗

-- 答案是构造函数不能是虚函数,析构函数可以是虚函数且推荐最好设置为虚函数

下面我们来看看为什么。首先,我们已经知道虚函数的实现则是通过对象内存中的vptr来实现的。而构造函数是用来实例化一个对象的,通俗来讲就是为对象内存中的值做初始化操作。那么在构造函数完成之前,vptr是没有值的,也就无法通过vptr找到作为虚函数的构造函数所在的代码区,无法执行 属于这个类自己的构造虚函数  , 所以构造函数只能作为普通函数存放在类所指定的代码区中。那么为什么析构函数推荐最好设置为虚函数呢?如文章开头的例子中,当我们delete(a)的时候,如果析构函数不是虚函数,那么调用的将会是基类base的析构函数。而当继承的时候,通常派生类会在基类的基础上定义自己的成员,此时我们当时是希望可以调用派生类的析构函数对新定义的成员也进行析构啦。

对于构造函数和析构函数与虚函数的关系我们也可以从另一方面来理解。通常是如例子中base *a=new(A)这样创建一个对象时调用构造函数,此时我们是在new函数中直接指定类名字的,当然会直接调用对应类的构造函数,不会出现基类类型实际指向子类的情况出现。而调用析构函数通常是针对对象的操作,如delete(a),此时我们当然希望可以调用到a实际指向的类型(类A)的析构函数,故需要设置为虚函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/738669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Acwing 848.有向图的拓扑序列

Acwing 848.有向图的拓扑序列 链接:848. 有向图的拓扑序列 - AcWing题库 /* 啥是拓扑排序? 首先要满足有向无环图的条件 一个有向图,如果图中有入度为 0 的点,就把这个点删掉,同时也删掉这个点所连的边。 一直进行上面出处理&am…

一个合格的测试人员需要具备迅速的反馈力

01 反馈指的是:在信息的传播中,接受者对传播者发出信息的反映。反馈得很重要一个属性就是时间滞延。在测试活动中,笔者经常会团队的测试人员一个问题:开发提交了一段代码后,多久能收到质量反馈?是按天&…

腾讯云轻量应用服务器性能测评一下,感觉还行吧

腾讯云轻量应用服务器性能如何?轻量服务器CPU内存带宽配置高,CPU采用什么型号主频多少?轻量应用服务器会不会比云服务器CVM性能差?腾讯云服务器网详解CPU型号主频、内存、公网带宽和系统盘存储多维对比,相对于CVM云服务…

Vue电商项目--登录与注册

登录注册静态组件 刚刚报了一个错误,找不到图片的资源 assets文件夹--放置全部组件共用静态资源 在样式当中也可以使用符号【src别名】。切记在前面加上 注册业务上 先修改原先的接口成这个按钮 然后把input框里面的数据保存到data中 注册业务下 就是点击获…

渠道归因(三)基于Shapley Value的渠道归因

渠道归因(三)基于Shapley Value的渠道归因 通过Shapley Value可以计算每个渠道的贡献权重,而且沙普利值的计算只需要参加的渠道总数,不考虑顺序,因此计算成本也较低。 传统的shapeley value import itertools from …

2.带你入门matlab数理统计常见分布的概率密度函数(matlab程序)

1.简述 计算概率分布律及密度函数值 matlab直接提供了通用的计算概率密度函数值的函数,它们是pdf 和namepdf函数,使用方式如下: Ypdf(‘name’,K,A,B)或者:namepdf (K,A,…

低代码平台之流程自动化测试

随着低代码平台的快速发展,开发人员可以便捷、快速地开发流程应用程序,由于业务流程的复杂化和业务需求的不断变化,对业务流程进行优化和改进将更加频繁,在这个过程中,就要求企业的流程测试的效率和质量需要跟上低代码…

Stable Diffusion - AWPortrait 1.1 模型与 Prompts 设置

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/131565908 AWPortrait 1.1 网址:https://www.liblibai.com/modelinfo/721fa2d298b262d7c08f0337ebfe58f8 介绍:AWPortrai…

java类的静态变量

java类的静态变量称为类变量,非静态变量称为实例变量。 静态变量可以在声明时初始化,也可以不在声明时初始化。 通过下面方式可以访问类的静态变量: 类内部直接访问静态变量;通过类名访问静态变量;通过实例访问静态变…

C++数据结构X篇_08_C++实现栈的顺序存储与链式存储

本篇参考C实现栈的顺序存储与链式存储整理,先搞懂结构框架,后期根据视频利用c对内容实现,也可以对c有更高的提升。 文章目录 1. 栈的顺序存储2. 栈的链式存储 栈是一种特殊的数据结构,栈中数据先进后出,且栈中数据只能…

30张图带你弄懂 二叉树、AVL、红黑树,他们之间有什么联系,AVL树和红黑树如何平衡

树(Tree)是若干个结点组成的有限集合,其中必须有一个结点是根结点,其余结点划分为若干个互不相交的集合,每一个集合还是一棵树,但被称为根的子树。注意,当树的结点个数为0时,我们称这…

[已解决]Running setup.py install for MinkowskiEngine ... error

虚拟环境中安装MinkowskiEngine: pip install -U MinkowskiEngine --install-option"--blasopenblas" -v --no-deps 报错:“Running setup.py install for MinkowskiEngine ... error” 解决办法[链接][参考1]: (1&…

Unity 编辑器-创建模板脚本,并自动绑定属性,添加点击事件

当使用框架开发时,Prefab挂载的很多脚本都有固定的格式。从Unity的基础模板创建cs文件,再修改到应有的模板,会浪费一些时间。尤其是有大量的不同界面时,每个都改一遍,浪费时间不说,还有可能遗漏或错改。写个…

查询直播频道发起的签到记录

接口描述 1、通过直播场次id,查询签到发起记录 2、接口支持https协议 接口URL http://api.polyv.net/live/v3/channel/chat/checkin-by-sessionId 请求方式 GET 请求参数描述 参数名必选类型说明appIdtrueString账号appIdtimestamptrueLong当前13位毫秒级时间戳&…

OSPF实验2

OSPF实验2 要求: 1.如图连接,合理规划IP地址,所有路由器各自创建一个loopback接口 2.R1再创建三个接口IP地址为201.1.1.1/24、201.1.2.1/24、201.1.3.1/24 R5再创建三个接口IP地址为202.1.1.1/24、202.1.2.1/24、202.1.3.1/24 R7再创建三…

小红书如何开店,送你一份保姆级开店教程

科思创业汇 大家好,这里是科思创业汇,一个轻资产创业孵化平台。赚钱的方式有很多种,我希望在科思创业汇能够给你带来最快乐的那一种! 今天,我们来谈谈小红书的电子商务。这也是今年非常流行的电子商务平台。很多人说…

《设计模式》责任链模式

《设计模式》责任链模式 定义: 责任链模式将链中每一个节点都看成一个对象,并且将这些节点对象连成一条链,请求会沿着这条链进行传递,直到有对象处理它为止,这使得多个对象都有机会接收请求,避免了请求发送…

warp框架教程3-path, method和自定义请求方法

path, method和自定义请求方法 path 是 warp 中的路由系统, 一个 web 框架的灵魂所在, 一个优美的路由系统可以给我们带来非常良好的使用体验, 而 warp 的路由体验本身就是非常 nice 的。在本文中将展示一个 RESTful 风格的 API 设计。下面先来学习一下 path 模块。 path 模块…

从小白到大神之路之学习运维第56天--------shell脚本实例应用2.0之有趣的知识

第三阶段基础 时 间:2023年7月10日 参加人:全班人员 内 容: shell实例 目录 shell脚本应用: 一、if判断 1、if判断的类型 1)单分支 2)双分支 3)多分支 2、单分支if判断 1&#x…

Could not increase number of max_open_files to more than 5000 (request: 65535)

修改MySQL 打开文件数量限制 修改内核限制 ulimit -n //查看系统限制 修改 /etc/security/limits.conf 添加 soft nofile 65530hard nofile 65535 mysql> SHOW VARIABLES LIKE open_files_limit; 通过 MySQL 命令行检查新限制。您可以使用以下查询,确保设置了新…