【C++】-多态的语法细节详解

news2024/10/11 0:27:10

在这里插入图片描述
💖作者:小树苗渴望变成参天大树🎈
🎉作者宣言:认真写好每一篇博客💤
🎊作者gitee:gitee✨
💞作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法🎄
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!

文章目录

  • 前言
  • 一、多态的概念?
    • 1.1 概念
  • 二. 多态的定义及实现
    • 2.1多态的构成条件
    • 2.2 C++11 override 和 final
    • 2.3 重载、覆盖(重写)、隐藏(重定义)的对比
  • 三、抽象类
    • 3.1 概念
    • 3 .2 接口继承和实现继承
  • 四、总结


前言

这篇我们开始讲解关于多态的语法细节,C++难学的第一个点在类和对象刚开始,第二个点就是在继承和多态,我们已经度过第一个点了,接下来要迈进我们第二个难关,大家要耐下心来进行学习,博主也会梳理好逻辑,方便大家学习,话不多说我们开始学习多态。


本章重点

  1. 多态的概念
  2. 多态的定义及实现
  3. 抽象类

一、多态的概念?

我们C++是一个面向对象的语言,我们通过类描述一个个的对象,但是每个对象可能有多种状态,多态是建立在继承的,我们来看看多态的例子:

1.1 概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
在这里插入图片描述
第一个例子: 比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票

第二个例子: 再举个栗子: 最近为了争夺在线支付市场,支付宝年底经常会做诱人的扫红包-支付-给奖励金的活动。那么大家想想为什么有人扫的红包又大又新鲜8块、10块…,而有人扫的红包都是1毛,5毛…。其实这背后也是一个多态行为。支付宝首先会分析你的账户数据,比如你是新用户、比如你没有经常支付宝支付等等,那么你需要被鼓励使用支付宝,那么就你扫码金额 = random()%99;比如你经常使用支付宝支付或者支付宝账户中常年没钱,那么就不需要太鼓励你
去使用支付宝,那么就你扫码金额 = random()%1;总结一下:同样是扫码动作,不同的用户扫得到的不一样的红包,这也是一种多态行为。ps:支付宝红包问题纯属瞎编,大家仅供娱乐。

通过这两个例子,我们应该知道为什么要有多态,是因为我们实现世界中有这种多种状态的情况

二. 多态的定义及实现

2.1多态的构成条件

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。


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 p;
	Student s;
	Func(p);
	Func(s);
	return 0;
}

在这里插入图片描述
这里引入一个概念:虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数。

并且静态的函数不能当成虚函数,虚函数不能定义和声明分离,只要普通函数可以当成虚函数去使用

通过这个例子相信大家应该知道多态的基本用法了,我们来看看构成多态的基本条件:

  1. 必须通过基类的指针或者引用调用虚函数(通过对象去调用时不行的)讲底层的时候在进行介绍
    在这里插入图片描述

  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
    在这里插入图片描述

基类的的函数必须加virtual,变成虚函数,子类必须重写基类的虚函数

虚函数重写的细节:

  1. 派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的
    返回值类型、函数名字、参数列表完全相同(即类型相同就行)),称子类的虚函数重写了基类的虚函数。
    在这里插入图片描述
  2. 子类的重写虚函数前面可以不用加virtual,但是建议加上(后面会介绍这个可能为析构函数为产生的)
    在这里插入图片描述

虚函数重写的两种例外:

  1. 协变(基类与派生类虚函数返回值类型不同)
    派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回必须任意基类对象的指针或者引用,派生类虚函数返回必须任意派生类对象的指针或者引用时,称为协变。(了解),引用我就不演示了效果是一样的。
    在这里插入图片描述
  2. 析构函数的重写(基类与派生类析构函数的名字不同)

在讲解这个例外之前在补充一个知识点,实现多态后:多态的调用看的是指向的对象,而不是看类型,而普通对象的调用时是看当前调用者的类型,可以简单理解,上面的多态通过指针或者引用去调用就是多态的调用,使用对象去调用就是对象的调用,想要达到通过指向的对象去调用就必须先形成多态才可以。(在讲解底层的时候会讲到,也就是分析为什么多态需要的指针或者引用去调用而通过对象去调用不行)

有了上面的补充我们来看例子:

class A
{
public:
	virtual ~A(){cout << "~A()" << endl;}
};
class B:public A
{
public:
	virtual ~B(){	cout << "~B()" << endl;}
};
int main()
{
	A a;
	B b;
	return 0;
}

在这里插入图片描述

在继承的使用那一篇说到过,我们析构函数都是先析构子在析构父,那我们上面这个看着有点奇怪,两个函数名不是一样的,而且没有返回值,这应该没有构成重写吧??所以这是一个例外,析构函数的重写可以没有返回值,而且在编译的时候父子类的析构函数名都被统一处理为了destructor,所以函数名也是一样的,够成虚函数的重写

如果不是虚函数的重写会发生情况??
在这里插入图片描述
我们看到运行结果一样的,有的人就会说那是不是虚函数都是一样的,那为什么要使他变成虚函数呢??
原因是要适应下面的场景:
在这里插入图片描述

我们发现它没有去调用B类的析构函数,p没有进行释放,造成内存泄漏了,为什么会这样??
我们的delete b实际上是两份构成b->destructor()+operator delete
我们new B一个对象,赋值给A*,它去调用函数是普通对象的调用,因为没有实现多态,看调用者的类型为A*,所以去调用了A类的析构函数,就永远调不到B类的析构,我们只要使父类的析构函数是虚函数,因为编译器本身会把所有类的析构函数统一处理成相同名字的函数,只要父类函数是虚函数,子类加不加virtual都构成虚函数的重写,此时就构成多态了,那么此时又是指针或者引用去调用,那么指向哪个对象就去调用哪个的函数
在这里插入图片描述
所以通过这个例子也说明了,多态调用和普通调用是不一样的,等底层原理的时候会给大家一一解释的。也解决了析构函数可以是虚函数,为什么要变成虚函数,原因就是为了解决上面的出现的情况,也解决了我上面说过为什么父类的virtual必须加,而子类的不需要,因为设计的时候,析构函数就不用加,也是防止写的人忘记加而导致的问题,后面为了保持一致,所以虚函数在重写的时候可加可不加。

所以我们在实现类继承或者多态的时候尽量都把基类的virtual给加上,防止出错

2.2 C++11 override 和 final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

  1. final:修饰虚函数,表示该虚函数不能再被重写
    在这里插入图片描述

博主认为一个虚函数不能被重写,那么它是虚函数的意义就没有了,虚函数就是为了多态的,多态就是需要虚函数的重写的,所以final用到的也特别少,反而下面这个关键字用到还是比较多的。

final还有一个用途:
设计一个类,不能被继承按照以往的案例,
(1)我们可以将构造函数私有化

class A
{
private:
	A(){}
};

我们没有办法创建类对象了,我们可以向外提供一个接口:

class A
{
public: 
	A createinit()
	{
		return A();
	}
private:
	A(){}
};

就面临一个问题,我们的createinit需要通过对象去调,而这个函数就是为了创建对象的,所以我们可以设置成静态的,通过类名去调用:

class A
{
public: 
	static A createinit()
	{
		return A();
	}
private:
	A(){}
};
int main()
{
	A::createinit();
	return 0;
}

(2)将析构函数私有化,就调用不到析构函数了
(3)使用final进行修饰类
在这里插入图片描述

final必须放在后面。

  1. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
    在这里插入图片描述

就是怕你想要fun是多态,但是基类忘记写了,代码少你可以一下子看出来,代码多了就不好看了。

2.3 重载、覆盖(重写)、隐藏(重定义)的对比

覆盖是底层的说法,重写是语法的说法
在这里插入图片描述

三、抽象类

3.1 概念

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

class Car
{
public:
	virtual void Drive() = 0;
};
class Benz :public Car
{
public:
	 virtual void Drive()
	 {
		 cout << "Benz-舒适" << endl;
	 }
};
class BMW :public Car
{
public:
	 virtual void Drive()
	 {
		 cout << "BMW-操控" << endl;
	 }
};
void Test()
{
	 Car* pBenz = new Benz;
	 pBenz->Drive();
	 Car* pBMW = new BMW;
	 pBMW->Drive();
}

在这里插入图片描述

3 .2 接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数

抽象类是多态的一种约束,你是抽象类,就必须使用多态,不然都实例化不出对象,就一点用都没有

四、总结

说到这里我们多态的具体使用细节就到这里了,说实话多态形成的条件还是特别多的,那条件多,就更来说明底层设计的更复杂,需要很多条件才能适应它涉及的场景,博主猜想:先设计出来多态,然后使用的时候发现需要这些条件才能满足,才导致多态的语法细节比较多,但是没事,博主会给大家讲解明白的,等我们学习了底层原理之后,就会恍然大悟,前面一会语法为什么要这样就解决了,我们今天就到此为止,我们下篇再见吧。。
请添加图片描述

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

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

相关文章

软路由系统 --- iKuai虚拟机修改虚拟机网卡类型

软路由系统&#xff1a;iKuai 注意&#xff1a;2.0系列及以后版本&#xff0c;默认安装成功后只是绑定了LAN口&#xff0c;其他的接口需要自己进爱快的WEB界面手工绑定。 虚拟机网卡类型说明&#xff1a; 个人版vmware 建议选择Linux ubuntu64位&#xff0c;否则默认网卡类型…

MySQL 数据抽稀 每分钟取一条

假如原始数据为每5秒一个数据&#xff0c;现在想展示为每4分钟一条数据&#xff0c;先按照分钟数把除以4余数为0的行选出来&#xff0c;在按照 年月日 时分&#xff0c;做组内排序&#xff08;窗函数ROW_NUMBER&#xff09;&#xff0c;最后再拿出序号为1的行。 WITH data_01 …

基于SpringBoot+vue的私人健身与教练预约管理系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

谈谈区块链技术

目录 1.什么是区块链 2.区块链的实现原理 3.区块链技术有哪些应用 4.区块链技术潜在的危害 5.区块链技术未来会怎么发展 1.什么是区块链 区块链是一种技术&#xff0c;它通过在一个分布式网络中记录和验证交易和数据&#xff0c;实现去中心化的数字账本。区块链的核心概念是…

读数据压缩入门笔记11_读后总结与感想兼导读

1. 基本信息 数据压缩入门 Understanding Compression [美]柯尔特麦克安利斯&#xff08;Colt McAnlis&#xff09;、[美]亚历克斯海奇 著&#xff1b; 王凌云 译 人民邮电出版社,2020年4月出版&#xff0c;1版 1.1. 读薄率 书籍总字数300千字&#xff0c;笔记总字数18197…

Linux用户权限

1.用户、权限、组的概念 1.1 用户 用户 是Linux系统工作中重要的一环&#xff0c;在Linux系统中&#xff0c;不论是由本机或是远程登录系统&#xff0c;每个系统都必须拥有一个账号&#xff0c;一个账号就是一个用户。 1.2 权限 在Linux系统中&#xff0c;每一个用户对不同…

汇编语言(第4版)实验5 编写、调试具有多个段的程序

&#xff08;1&#xff09;参考答案&#xff1a; ①不变&#xff08;0123h,0456h,0789h,0abch,0defh,0cabh,0987h&#xff09; ②076c 076b 076a ③X-2 X-1 &#xff08;2&#xff09;参考答案&#xff1a; ①不变&#xff08;0123h,0456h&#xff09; ②076c 076b 076a ③X-2…

AcWing 1275. 最大数—线段树单点修改

题目链接 AcWing 1275. 最大数 题目描述 分析 这道题是线段树的模板题&#xff0c;属于一眼ding真&#xff0c;鉴定为线段树的裸题&#xff0c;正好用来学习线段树。线段树、树状数组等数据结构题&#xff0c;难的地方是在如何分析出来要用哪个数据结构解决。 线段树要比树状…

上海VR全景展示,快速了解VR全景拍摄

导语&#xff1a; 随着科技的不断进步&#xff0c;虚拟现实技术的应用日益广泛。在这其中&#xff0c;VR全景图片作为一种数字化助力的全景拍摄方式&#xff0c;正逐渐成为人们关注的焦点。通过数字化技术&#xff0c;VR全景图片能够以360度全方位的视角呈现真实的场景&#x…

docker学习和进阶2023

文末有下载地址。如有侵权请联系作者谢谢&#xff01; docker学习和进阶2023 参考引用 docker学习笔记&#xff1a;https://blog.csdn.net/m0_46188681/article/details/128993319 Docker容器配置和资源限制&#xff1a;https://www.cnblogs.com/xiugeng/p/16254087.html 容…

idea使用命令将jar包导入到maven仓库中

因为今天突然忘了命令&#xff0c;记下来方便以后查看 pom文件的依赖 jar包路径 进入idea中命令窗 输入命令 mvn install:install-file -DfileD:\Project\spring-cloud\dubbo-api\target\dubbo-api-1.0-SNAPSHOT.jar -DgroupIdcom.wmx -DartifactIddubbo-api -Dversion1.0…

flutter数字动画库:animated_flip_counter

前言 在数字动画的制作中&#xff0c;有时候我们会面临时间紧张或效果不尽如人意的情况。这时&#xff0c;使用现成的动画库或工具可以大大提高效率&#xff0c;同时也能够获得更好的效果。animated_flip_counter就是一个非常不错的数字动画库&#xff0c;可以帮助我们快速地创…

Leetcode-每日一题【剑指 Offer II 010. 和为 k 的子数组】

题目 给定一个整数数组和一个整数 k &#xff0c;请找到该数组中和为 k 的连续子数组的个数。 示例 1&#xff1a; 输入:nums [1,1,1], k 2输出: 2解释: 此题 [1,1] 与 [1,1] 为两种不同的情况 示例 2&#xff1a; 输入:nums [1,2,3], k 3输出: 2 提示: 1 < nums.leng…

SIP视频对讲sip广播网关

SV-PA2是专门对行业用户需求研发的一款SIP音视频对讲&#xff0c;媒体流传输采用标准IP/RTP/RTSP协议。它很好的继承了锐科达话机稳定性好、电信级音质的优点&#xff0c;且完美兼容当下所有基于SIP的主流IPPBX/软交换/IMS平台,如Asterisk, Broadsoft, 3CX, Elastix 等。它集多…

linux 使用nethogs命令查看各个进程使用网络的情况

1&#xff0c;使用nethogs命令查看各个进程使用网络的情况 #yum -y install nethogs 2&#xff0c;使用nethogs命令查看进程使用网络的情况 #nethogs

【Docker】Docker部署私有仓库的配置及应用

文章目录 一、Docker-registry 搭建本地私有仓库1. Registry 的概念2. Registry 的部署过程 二、Docker-harbor 搭建私有仓库1. 什么是Harbor2. Harbor 的特性3. Harbor的构成4. Harbor 的部署过程4.1 安装 harbor4.2 创建项目并进行上传下载4.3 上传镜像到私有仓库4.4 从私有仓…

LLM Data Pipelines: 解析大语言模型训练数据集处理的复杂流程

编者按&#xff1a;在训练大语言模型的过程中,构建高质量的训练数据集是非常关键的一步&#xff0c;但关于构建大模型训练所需数据集的通用数据处理流程&#xff08;Data pipelines)的相关资料极为稀少。 本文主要介绍了基于Common Crawl数据集的数据处理流程。首先,文章概述了…

Java版知识付费源码 Spring Cloud+Spring Boot+Mybatis+uniapp+前后端分离实现知识付费平台 +支持二次开发定制

提供职业教育、企业培训、知识付费系统搭建服务。系统功能包含&#xff1a;录播课、直播课、题库、营销、公司组织架构、员工入职培训等。 提供私有化部署&#xff0c;免费售后&#xff0c;专业技术指导&#xff0c;支持PC、APP、H5、小程序多终端同步&#xff0c;支持二次开发…

SNMP协议是什么?

SNMP协议是什么&#xff1f; 简单网络管理协议&#xff08;Simple Network Management Protocol&#xff0c;SNMP&#xff09;是由互联网工程任务组定义的一套网络管理协议。该协议是基于简单网关监视协议&#xff08;Simple Gateway Monitor Protocol&#xff0c;SGMP&#x…

芯片制造详解.晶圆的诞生.学习笔记(二)

芯片制造详解.晶圆的诞生.学习笔记.二 一、硅片的制造二、硅片、晶圆和芯片的区别三、从硅棒到晶圆(1).截断(2).四探针法(3).裁成硅段(4).滚磨(5).研磨定位边(槽)(6).硅段切片(7).磨片(8).倒角(9).打上激光标识码(10).刻蚀(11).化学机械抛光(CMP)(12).湿法清洗(13).检测 包装 四…