P20 C++虚函数与纯虚函数

目录

前言

01 虚函数能干什么呢?

02 没有虚函数前的例子

03 使用虚函数后的例子

虚函数virtual 概念

虚函数使用需要一定开销


前言

本期我们学习的是 C++ 中的虚函数。

过去的几期,我们一直在讨论类、面向对象编程、继承这些内容,所有的这些内容,包括本期我们将要学习的虚函数,对整个面向对象的概念都非常重要。

虚函数是指一个类中你希望重载的成员函数 ,当你用一个基类指针或引用指向一个继承类对象的时候,调用一个虚函数时, 实际调用的是继承类的版本。

01 虚函数能干什么呢?

虚函数允许我们在子类中重写方法。

假设我们有两个类 A 和 B,B 是 A 派生出来的类。如果我们在 A 中创建一个方法,标记为 virtual,我们可以选择在 B 类中重写那个方法,让它做其他的事情。

像之前一样,我们通过一个例子来解释今天的知识点。

02 没有虚函数前的例子

我创建了两个类,一个是 Anima,它唯一拥有的是一个名为 GetName 的公共方法,它会返回一个字符串,我们让它返回 "animal"。

还有另外一个类 Dog,它是 Animal类的子集,它提供一个构造函数,允许我们指定一个名字;然后给它提供了一个叫 GetName 的方法,我们让他返回Dog字符串。

我们来看看如何使用上面这些设定。

我有一个 Print 函数,参数是一个 Animal的指针。

现在我们有了一个函数,它可以接受任何 Animal类型的参数,你可以看到,我们不会得到任何的编译错误。

当我们试图将 a 传递给函数时,因为 p 是一个 Animal,Dog 是 Animal的·子类,在函数里面我们做的就是调用 GetName 方法,我们期望的是,在主函数中调用的部分,参数为 Animal类型时,GetName 用于 Anima,而参数为 DOg类型时,GetName 用于 Dog。

然而,运行代码之后你会发现它打印了两次 Animal。

//测试代码
//执行结果为打印两次Animal#include <iostream>class Animal
{
public:std::string getName(){return "Animal";};
};class Dog: public Animal
{
public:std::string getName(){return "Dog";};
};void print(Animal* obj)
{std::cout<<obj->getName()<<std::endl;
}int main()
{Animal *animal = new Animal;Dog *dog = new Dog;print(animal);print(dog);return 0;   
}

为什么会这样呢?

03 使用虚函数后的例子

发生这种情况的原因时,在我们声明函数时,我们的方法通常在类内部起作用。然后当调用方法的时候,会调用属于该类型的方法。

我们看这个 Print 函数,它的参数是 Animal,这意味着当我们调用 GetName 函数时,如果是在 Animal里面,那么它会从 Animal类中找这个叫做 GetName 的函数。

然而,我们希望 C++ 能意识到一点:我在这里传递的 Animal实际上是 它的子类Dog,所以,请调用 Dog中的 GetName 函数。

这时候,虚函数就该出现了。

虚函数virtual 概念

虚函数引入了一种叫做 Dynameic Dispatch(动态联编)的东西,它通常通过 V 表(虚函数表)来实现编译。

V 表就是一个表,它包含基类中所有虚函数的映射,这样我们可以在它运行时,将它们映射到正确的覆写(overwrite)函数。

简单起见,现在你只需要知道,如果你想覆写一个函数,必须将基类中的基函数标记为虚函数。

我们回到代码中继续看一下。

我在基类 Animal类中 GetName 函数前面使用了 virtual 这个关键字,这可以告诉编译器,——嘿,为这个函数生成 V 表吧,这样,如果它被重写了,你可以指向正确的函数。

我们运行代码试试看。

我们得到了期望的结果。

现在,我们可以做的另一件事:使用在 C++11 引入的覆写函数标记的关键字 override。

这个不是必须的,无论有没有这个关键字,程序都会正常工作,但是我还是建议你这样做。因为首先这会让你的程序更具有可读性,阅读程序的时候我们可以知道这实际上是一个覆写的函数;它还可以帮助我们预防 Bug 的发生,比如拼写错误之类的

//用于测试的代码
#include <iostream>class Animal
{
public:virtual std::string getName(){return "Animal";};
};class Dog: public Animal
{
public:std::string getName(){return "Dog";};
};void print(Animal* obj)
{std::cout<<obj->getName()<<std::endl;
}int main()
{Animal *animal = new Animal;Dog *dog = new Dog;print(animal);print(dog);return 0;   
}

虚函数使用需要一定开销

这就是虚函数的本质,但是很遗憾的一点是,虚函数并不是没有额外的开销的,有两种与虚函数相关的运行时成本。

首先,我们需要额外的内存来存储 V 表,这样我们就可以分配到正确的函数,包括基类中要有一个成员指针指向 V 表;其次,每次我们调用虚函数时,我们需要遍历这个表来确定要映射到哪个函数,这些是额外的性能损失。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS应用开发者基础认证【题库答案】

HarmonyOS应用开发者高级认证【题库答案】 一、判断 首选项preferences是以Key-Value形式存储数据&#xff0c;其中Key是可以重复。&#xff08;错&#xff09;使用http模块发起网络请求时&#xff0c;必须要使用on(‘headersReceive’&#xff09;订阅请求头&#xff0c;请…

Python生日蛋糕

系列文章 序号文章目录直达链接1浪漫520表白代码https://want595.blog.csdn.net/article/details/1306668812满屏表白代码https://want595.blog.csdn.net/article/details/1297945183跳动的爱心https://want595.blog.csdn.net/article/details/1295031234漂浮爱心https://want…

Linux系统---僵尸进程、孤儿进程

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C/C》 键盘敲烂&#xff0c;年薪百万&#xff01; 有了上一篇博客的学习&#xff0c;我们已经简单了解了进程的基础知识&#xff0c;今天我们再来学习两个特殊的进程&#xff0c;僵尸进程和孤儿进程。 …

电力感知边缘计算技术网关产品设计方案-硬件方案

网关硬件架构设计图: 1.配置方案 配置差异 A类网关 B类网关 CPU

SpringBoot+Redis编写一个抢红包雨的案例。附源码。

案例演示 SpringBootRedis编写一个抢红包雨的案例。附源码 1、案例分析&#xff0c;整体方案介绍 预备上线一个红包雨活动。这个红包雨的思路是活动开始前25分钟&#xff0c;在后台创建活动。然后前端用户进入&#xff0c;到点后将设置的金额拆分成多个小红包&#xff0c;开启倒…

Redis面试题:分片集群相关问题

目录 面试官&#xff1a;redis的分片集群有什么作用 面试官&#xff1a;Redis分片集群中数据是怎么存储和读取的&#xff1f; 面试官&#xff1a;redis的分片集群有什么作用 候选人&#xff1a;分片集群主要解决的是&#xff0c;海量数据存储的问题&#xff0c;集群中有多个m…

函数指针数组指针数组传参的本质字符指针

&#x1f680; 作者&#xff1a;阿辉不一般 &#x1f680; 你说呢&#xff1a;不服输的你&#xff0c;他们拿什么赢 &#x1f680; 专栏&#xff1a;爱上C语言 &#x1f680;作图工具&#xff1a;draw.io(免费开源的作图网站) 如果觉得文章对你有帮助的话&#xff0c;还请点赞…

redis报错3

INFO: Initializing SpringDispatcherServletdispatcherServlet

Linux网络——数据链路层

目录 一.认识以太网 二.以太网帧格式 三.认识MAC地址 四.认识MTU 五.以太局域网的通信原理 六.其他重要协议 1.DNS协议 2.域名简介 3.ICMP协议 4.NAT技术 5.NAT技术的缺陷 6.NAT和代理服务器 一.认识以太网 "以太网" 不是一种具体的网络, 而是一种技术标…

Redis实战篇(一)短信登录

Redis实战篇&#xff08;一&#xff09;短信登录 1.1、导入黑马点评项目 1.1.1 、导入SQL 1.1.2、有关当前模型 手机或者app端发起请求&#xff0c;请求我们的nginx服务器&#xff0c;nginx基于七层模型走的事HTTP协议&#xff0c;可以实现基于Lua直接绕开tomcat访问redis&a…

TUP通信

一&#xff0c;概括 二&#xff0c;常用方法 三&#xff0c; 实现步骤&#xff08;一发一收&#xff09; 四&#xff0c;案例&#xff08;一接一收&#xff09; &#xff08;1&#xff09;&#xff0c;客户端 &#xff08;2&#xff09;&#xff0c;服务端 &#xff08;3&…

css实现鼠标移入背景图片变灰并浮现文字的效果

首先上效果图 说明一下我的html结构 如上图是一个div包裹的img标签, div的块大小width, height 自己定义, 我说明一下核心样式代码 下面写法是scss, 请自行替换 .web-query-image {position: relative; // 相对定位, 方便浮现文案进行绝对定位border-radius: 8px;box-sizing: …