C++_多态

目录

1、什么是虚函数

1.1 什么是虚函数重写

1.2 虚函数的继承

1.3 协变 

1.4 析构函数的重写

2、override和final

2.1 final

2.2 override

3、纯虚函数/抽象类

3.1 接口继承和实现继承 

4、多态的原理


前言:

        在C++中,多态指的是调用同一个类的成员函数时,会产生两种不同的结果,因此多态又称多种形态。

实现多态的前提是:

        1、两个类必须满足继承关系。

        2、必须通过基类的指针或引用去调用该类的成员函数,并且这个成员函数是被virtual修饰过的虚函数,还得完成虚函数的重写

        多态结构示意图如下: 

1、什么是虚函数

        被关键字virtual修饰的类成员函数就是虚函数,比如下面的BuyTicket就是一个虚函数。

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

1.1 什么是虚函数重写

       在基类和派生类中都存在一个相同的虚函数(即函数名、函数形参类型及个数、返回类型都相同),这时候把派生类中的虚函数叫做基类虚函数的重写

        注意:虚函数重写和虚函数是两个概念,即虚函数不一定构成虚函数重写,构成虚函数重写的前提是两个函数必须是虚函数。

        体现虚函数重写的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};void Func(Person& p)
{ p.BuyTicket(); 
}
int main()
{Person ps;Student st;Func(ps);Func(st);return 0;
}

        运行结果:

        从结果可以看到,虚函数重写的目的就是为了实现多态,上面代码通过同一个形参p调用两个不同的函数BuyTicket,从而达到实现不同的功能。

1.2 虚函数的继承

        在继承关系中,派生类可以继承基类的成员,那么虚函数也是可以被继承的,比如拿上述代码举例,Student类中如果没有对BuyTicket函数进行virtual修饰,却同样可以实现多态的效果,原因就是派生类继承了基类的虚函数virtual,让自己的BuyTicket也变成了一个虚函数。


         但是如果基类中的BuyTicket函数不是虚函数,而派生类的BuyTicket是虚函数则无非满足虚函数的重写。

1.3 协变 

        从上文可以得知,虚函数重写的要求是:函数名、函数形参类型及个数、返回类型都相同,则协变就是在此基础上放宽了对虚函数重写的要求,即返回类型不相同的虚函数也可以构成重写,但是返回的必须是指针或者引用,并且这两个虚函数的返回值是继承关系。

        协变示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;class Person {
public:virtual Person& BuyTicket() { cout << "买票-全价" << endl; return *this; }
};
class Student : public Person {
public:virtual Student& BuyTicket() { cout << "买票-半价" << endl; return *this;}
};void Func(Person& p)
{ p.BuyTicket(); 
}
int main()
{Person ps;Student st;Func(ps);Func(st);return 0;
}

        运行结果:

        协变示意图如下:

1.4 析构函数的重写

        析构函数的重写可以不满足协变和虚函数重写的要求,只需要对两个类的析构函数加上virtual即可实现重写。原因在于派生类和基类的析构函数都会被统一处理成destructor的函数名,这时候他们就满足了虚函数重写的要求(此时函数名相同了)。

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;class Person {
public:virtual ~Person() { cout << "~Person()" << endl; }
};
class Student : public Person {
public:virtual ~Student() { cout << "~Student()" << endl; }
};int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}

        运行结果:

2、override和final

        override和final的目的就是手动的对虚函数重写进行严格的检查,因为在实现虚函数重写的时候有些疏忽编译器是不会报错的,因此我们可以使用override和final严格要求虚函数的重写。

2.1 final

        修饰一个虚函数,让其不能被重写。

        示例代码:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;class Person
{
public:virtual void Number() final {}
};
class Student :public Person
{
public:virtual void Number() {}//由于基类在虚函数后面加了final,因此此处不能被重写
};int main()
{return 0;
}

         运行结果:

2.2 override

        override的作用是检查被修饰的虚函数是否重写了基类的某个虚函数。

        override示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;class Person
{
public://virtual void Number()  {}
};
class Student :public Person
{
public://Person类中没有实现Number虚函数,因此此处没有实现虚函数的重写virtual void Number()override {}
};int main()
{return 0;
}

        报错原因:

3、纯虚函数/抽象类

        在虚函数的声明末尾处写上“=0”,则该虚函数为纯虚函数。而这个纯虚函数所在的类称之为抽象类,即不能用该类实例化对象,并且继承该抽象类的派生类也不能实例化出对象。

        纯虚函数/抽象类的写法:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;class Person
{
public:virtual void Number()=0  {}
};
class Student :public Person
{
public://virtual void Number() {}
};int main()
{Person p;Student s;//Person和Student都不能实例化出对象return 0;
}

        只有重写了该抽象类中的纯虚函数,才能用派生类实例化出对象(基类还是不能实例化对象):

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;class Person
{
public:virtual void Number()=0  {}
};
class Student :public Person
{
public:virtual void Number() {}//重写纯虚函数
};int main()
{//Person p;//Person依旧不能实例化对象Student s;//由于Student中的Number重写了Person的Number,因此Student可以实例化对象return 0;
}

3.1 接口继承和实现继承 

        实现继承就是我们所说的派生类继承了基类的成员函数,继承的是该函数的整体。而接口继承是虚函数继承,虚函数继承的只是函数的接口部分,并不是整个函数。他们是有区别的。

        体现他们区别的示意图如下:

4、多态的原理

        实现多态的根本原因是虚函数的重写,而在底层中,在基类部分会自动生成一个数组指针(又称虚表指针),该指针指向的数组是用来存放虚函数地址的(这个生成动作在编译阶段就已经完成了),所以该数组又称虚函数表

        因此当调用条件满足多态时,编译器实际上会到基类中找到虚表指针,然后调用的是该指针指向的虚函数,这也是为什么通过同一个接口可以实现两个不同结果。

        可以通过观察一个类的大小来发现虚指针的存在:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};int main()
{Person ps;cout << sizeof(ps) << endl;//ps的大小为4return 0;
}

        通过监视窗口也可以发现虚表指针的存在:


        如果是继承关系,则虚表指针存放在父类中的基类部分中,这也是为什么实现多态的条件之一是需要基类指针调用虚函数,因为虚表指针存在基类部分中,使用基类指针刚好可以与其对应。

结语: 

        以上就是关于多态的讲解, 多态的本质就是继承,并且需要满足虚函数重写和基类指针、引用调用,多态实际上只需要记住这两点即可,只不过在此基础上可以延申出更多的细节。最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!

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

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

相关文章

Windows系统安装Flink及实现MySQL之间数据同步

Apache Flink是一个框架和分布式处理引擎&#xff0c;用于对无界和有界数据流进行有状态计算。Flink的设计目标是在所有常见的集群环境中运行&#xff0c;并以内存执行速度和任意规模来执行计算。它支持高吞吐、低延迟、高性能的流处理&#xff0c;并且是一个面向流处理和批处理…

re:从0开始的CSS学习之路 3. CSS三大特性

0. 写在前面 很多的学习其实并不知道在学什么&#xff0c;学一个新东西学着学着就变成了抄代码&#xff0c;背概念。把看视频学习变成了一个赶进度的任务&#xff0c;到头来只学到了一些皮毛。 文章目录 0. 写在前面1. CSS三大特性——层叠性2. CSS三大特性——优先级3. CSS三…

学习Spring的第十六天

AOP底层两种生成Proxy的方式 我来解释这两种方式 1 目标类有接口 , 调用JDK的动态代理实现 2 目标类没有接口 , 用Cglib实现 , 即生成目标类的子类 , 来实现动态代理 , 所以要求目标类不能时final修饰的 . (若有接口 , 也可用Cglib方式实现 , 需要手动配置<aop: config pr…

完全二叉树的结点个数

给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的若干位置。若最…

java SpringBoot2.7整合Elasticsearch(ES)7 带条件分页查询与不带条件分页查询演示讲解

上文 java SpringBoot2.7整合Elasticsearch(ES)7 进行文档增删查改 我们带着大家 整合了 Elasticsearch 对索引中的文档做了 各方面操作 然后 我们来说说 分页查询 这里 为了方便大家看 我加了五条数据进去 这里 我们仍然需要带个条件 这里 我们用name Mapper 接口 加一个这…

双非本科准备秋招(18.2)—— 图解Monitor

对象头 普通对象&#xff1a; 数组对象&#xff1a; java中对象存储结构分为对象头&#xff08;Header&#xff09;、实例数据&#xff08;Instance Date&#xff09;和对齐填充&#xff08;Padding&#xff09;。 对象头存储着Mark Word和Klass Word&#xff0c;通过Klass Wo…

复选框和单选按钮——WindowsForm系列教程

你好&#xff0c;这里是BIM的乐趣&#xff0c;我是九哥~ 很多程序的GUI中都有两个常见小部件&#xff1a;单选按钮和复选框。 这些是直观地向用户提供多种选择的方法。我敢肯定&#xff0c;你们都熟悉这些形式的输入&#xff0c;但复选框允许用户打开和关闭个别选项&#xff…

Verilog刷题笔记18

题目&#xff1a;An if statement usually creates a 2-to-1 multiplexer, selecting one input if the condition is true, and the other input if the condition is false. 解题&#xff1a; module top_module(input a,input b,input sel_b1,input sel_b2,output wire ou…

聚焦网络安全公司,看F5如何应对企业数字化挑战

应用无处不在的当下&#xff0c;从传统应用到现代应用再到边缘、多云、多中心的安全防护&#xff0c;安全已成为企业数字化转型中的首要挑战。有专家指出&#xff0c;目前网络安全市场已经是仅次于计算、存储、网络的第四大IT基础设施市场。那什么网络安全公司应该具有哪些能力…

vue3 使用defineAsyncComponent 动态加载组件

问题场景 在项目中使用静态加载组件基本能覆盖80%的场景了&#xff0c;如下图 但是我们在需要 循环生成一些的component 的时候或者在 开发ssr服务端渲染的页面 就会遇到有些组件以静态方式导入就会报错&#xff0c;导致进程失败&#xff0c;那么这时候就需要用到动态组件。那…

第8节、双电机多段直线运动【51单片机+L298N步进电机系列教程】

↑↑↑点击上方【目录】&#xff0c;查看本系列全部文章 摘要&#xff1a;前面章节主要介绍了bresenham直线插值运动&#xff0c;本节内容介绍让两个电机完成连续的直线运动,目标是画一个正五角星 一、五角星图介绍 五角星总共10条直线&#xff0c;10个顶点。设定左下角为原点…

入侵报警系统行业研究:智能化潮流助力市场维持正增长

侵报警系统intruder alarm system(IAS)利用传感器技术和电子信息技术探测并指示非法进入或试图非法进入设防区域(包括主观判断面临被劫持或遭抢劫或其他危急情况时&#xff0c;故意触发紧急报警装置)的行为&#xff0c;处理报警信息、发出报警信息的电子系统或网络。 当入侵行为…

解析与模拟常用字符串函数strcpy,strcat,strcmp,strstr(一)

今天也是去学习了一波字符串函数&#xff0c;想着也为了加深记忆&#xff0c;所以写一下这篇博客。既帮助了我也帮助了想学习字符串函数的各位。下面就开始今天的字符串函数的学习吧。 目录 strcpy与strncpy strcat与strncat strcmpy strstr strcpy与strncpy 在 C 语言中&…

哪些洗地机比较好?家用洗地机选购攻略

洗地机集合了拖把跟吸尘器的功能&#xff0c;面对地面上的水渍、油污脏东西可以快速的清洁干净。从去年开始洗地机领域的竞争就变得异常激烈。各大厂家纷纷推出各自的主打型号&#xff0c;有的注重续航&#xff0c;有的突出清洁效能&#xff0c;还有的专注于性价比。相较于前几…

谷歌seo搜索引擎优化教程有吗?

教程&#xff0c;教学&#xff0c;指南&#xff0c;这些东西哪里都有&#xff0c;尤其是关于seo相关方面的&#xff0c;这些可以说到处都是&#xff0c;能把谷歌seo这个关键词做上去的&#xff0c;可以说就是实力的证明了&#xff0c;在这里我们说一个无论是老手还是新手都应该…

【知识图谱+大模型的紧耦合新范式】Think-on-Graph:解决大模型在医疗、法律、金融等垂直领域的幻觉

Think-on-Graph&#xff1a;解决大模型在医疗、法律、金融等垂直领域的幻觉 Think-on-Graph 原理ToG 算法步骤&#xff1a;想想再查&#xff0c;查查再想实验结果 论文&#xff1a;https://arxiv.org/abs/2307.07697 代码&#xff1a;https://github.com/IDEA-FinAI/ToG Think…

PYthon进阶--网页采集器(基于百度搜索的Python3爬虫程序)

简介&#xff1a;基于百度搜索引擎的PYthon3爬虫程序的网页采集器&#xff0c;小白和爬虫学习者都可以学会。运行爬虫程序&#xff0c;输入关键词&#xff0c;即可将所搜出来的网页内容保存在本地。 知识点&#xff1a;requests模块的get方法 一、此处需要安装第三方库reques…

dump分析方法

一、关于dump 1、什么是dump 在计算机领域中&#xff0c;术语“dump”通常用来指代将某种数据以某种格式进行转储或导出的过程。这个术语可以用于多种不同的上下文&#xff0c;下面是一些常见的情况&#xff1a; 内存转储&#xff08;Memory Dump&#xff09;&#xff1a;在…

网络空间内生安全数学基础(2)——编码信道数学模型

目录 &#xff08;零&#xff09;这篇博客在干什么&#xff08;一&#xff09;内生安全与香农信道编码定理&#xff08;二&#xff09;基本定义&#xff08;三&#xff09;编码信道存在定理&#xff08;三.壹&#xff09;编码信道存在第一定理&#xff08;三.贰&#xff09;编码…

Micro micro controller一览

https://www.microchip.com.cn/&#xff0c; Microchip中文网站 https://www.microchip.com.cn/newcommunity/index.php?mSearch&adosearch&moduleDownload&keyworddsPIC33&p3 Microcontrollers and microProcessors dsPIC33 Digital Signal Controllers (D…