【C++】多态的使用详解

本篇要分享的内容是多态,以下为本篇目录。

目录

1.多态的概念

2. 多态的定义及实现

3.虚函数

4.C++11  override和final

4.1final关键字

4.2override关键字

 5.抽象类

5.1抽象类的概念

5.2接口继承和实现继承


1.多态的概念

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会
产生出不同的状态。

比如旅行景点的成人票是全票,儿童半票,军人优先购票;

又比如拼多多的红包,新用户就会获得很多福利;而老用户的红包福利只有一点点。

像这样不同的身份可以产生不同的行为和结果,也是一种多态行为。

2. 多态的定义及实现

用一段简单的代码来认识多态

#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;
}

可以看到在多态中引入了一个新的概念: 虚函数(即在函数前加上virtual)。

并且在函数测试中使用父类创建了对象,并且调用函数,这时要注意我们在main函数中分别用父类和子类创建了对象,并调用了Func函数。

在之前的继承中我们知道,父类的引用即可传父类对象,也可以传子类对象

这时运行代码观察结果

 

可以看到这里调用了两个函数:
普通人买票全价,学生买票半价;

但是当我们将切片改为传值传参,而不是传引用传参时结果就会调用同一个函数

可以看到这里去掉了&符号

运行结果如下

可以看到调用到的时一个函数

那这就证明出形成多态的条件:

①.虚函数重写

②.父类的指针或者函数去调用虚函数

以上条件只要有一个不满足,就会变成普通调用,就会去看对象的类型

3.虚函数

在之前的继承中我们学到过虚继承的关键字也是virtual,但是和我们今天在多态中所学的虚函数是没有关系的,如同取地址符号&,和引用符号&,虽然符号相同,但是是有区别的。

要构建多态的第一个条件就是构成虚函数重写,那么构成虚函数重写也是有条件的:

继承关系中两个父子类关系的虚函数,函数名、参数、返回值,都要相同,才能完成虚函数的重写。

当然,virtual只能修饰成员函数,它的作用是修饰成员函数来构成多态,在类外使用当然是会直接报错的。

但是上述的三同(函数名、参数、返回值相同)又有一个例外,称之为协变:返回值可以不同,但是必须是父子类关系的指针或引用

如图

可以看到我们修改了函数的返回值是父子关系类的指针,结果也会构成多态。

但是在如上的这中情况下,子类又可以不加virtual

为什么要这样设计呢?

这里的函数需要重写,重写就是重复实现,也可以认为在父类中的虚函数,子类与父类同名的虚函数virtual也会继承下来。

以上的两种特殊用法是需要记忆的,是可以使用,但是在我们自己编写代码中最好还是使用三同来完成多态。

接下来则是析构函数,有如下场景

class Person {
public:~Person() { cout << "~Person()" << endl; }
};
class Student : public Person {
public:~Student() { cout << "~Student()" << endl; }
};
int main()
{Person* p1 = new Person;delete p1;Person* p2 = new Student;delete p2;return 0;
}

这里的分别在两个类中定义了两个析构函数,为方便观察我们输出他们。

再在main函数中分别定义两个指针,并且分别指向父类和子类并析构

运行结果如下 

 可以看到创建的两个指针指向的类不同,但是却同时调用了父类的析构函数,原因是在没有使用virtual修饰函数的情况下,所有的析构函数都被编译器命名为:destroy(),所以在两个不同的类中相当与重写了析构函数。

因为子类不能正常调用析构函数,所以有可能会造成内存泄漏。

 所以我们需要使用虚函数就可以使他们分别调用自己类中的析构函数

(子类调用析构函数是先子后父)。

4.C++11  override和final

在C++11中更新了两个关键字final和override;

4.1final关键字

1.final修饰类时,这个类不能被继承

2.final修饰虚函数时,这个虚函数不能被重写

用法如下

class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() { cout << "Benz-舒适" << endl; }
};

此处实在Car类中的虚函数后加上final {};

 可以看到使用final之后,这个虚函数就不能被重写了

并且这个函数必须是虚函数

 这里报错很明显,final不能修饰非虚函数。

在Car类后加上final之后这个类就不能被继承了 。

4.2override关键字

override用来修饰派生类的虚函数,用来检测是否完成重写。

以下是override的使用位置

class Car
{public:virtual void Drive(){}
};class Benz :public Car
{public:virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

如果没有完成重写就会报错

虚函数一定是要重写的,否则虚函数是没有意义的。

 5.抽象类

5.1抽象类的概念

上面我们提到过虚函数。在虚函数后面加上=0,则这个函数为纯虚函数

那么此时,包含整个纯虚函数的类叫做抽象类,也叫做接口类,抽象类不能实例化对象。 

可以看到上图的Car中有纯虚函数,Car就变成了抽象类,此时Car也就不能定义对象,但是可以定义指针。

 

上图中我们定义了新的类,并且继承了抽象类Car,可以看到此时新的类也不能实例化对象了; 

但是我们可以将纯虚函数重写,这样就可以实例化对象了

所以有以下使用场景

class Car
{
public:virtual void Drive() = 0;
};class Benz :public Car
{
public:void Drive(){cout << "Benz" << endl;}
};class BMW :public Car
{
public:virtual void Drive(){cout << "BMW-操控" << endl;}
};void func(Car* c)
{c->Drive();
}
int main()
{func(new Benz);func(new BMW);
/*Car* pBenz = new Benz;
pBenz->Drive();
Car* pBMW = new BMW;
pBMW->Drive();*/
}

在以上代码中我们发现在func函数中的参数定义了抽象类的指针对象c,利用c去调用类中重写的函数。

同时在main函数中调用函数,不难看出指向哪个子类就调用的是哪个子类重写的函数。

所以我们可以得出的结论是抽象类强制了子类去重写。

5.2接口继承和实现继承

普通函数的继承是一种实现继承

虚函数的继承是一种接口继承

用代码说明

首先是实现继承,我们在父类中简单写了一个输出函数,他会直接继承到子类中,也就是说子类可以直接对func函数直接进行调用,可以理解为一种函数的复用。

而虚函数的接口继承,相当于需要在子类中重写函数的实现,但是调用的参数,或者说是接口,还是父类的接口,所以我们才需要重写虚函数。

最后用一道小题来了解接口继承

class A{public:virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}virtual void test(){ func();}};class B : public A{public:void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }};int main(int argc ,char* argv[]){B*p = new B;p->test();return 0;}A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确

首先观察main函数中使用B类创建了指针p;

使用p去调用test,test在父类中,所以test中的this指针是指向A类

在test中又调用了func函数

因为func在A类中被定义为虚函数,并且在B类中重写,在加上虚函数为接口继承,

虚函数只重写函数体内容,而接口还是父类的接口

所以答案为:B->1

以上就是本篇要分享的关于多态的概念和简单实用,本人水平有限,尽管不遗余力但本篇的内容仍有不足,还请读者指正,感谢您的阅读。

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

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

相关文章

RedisInsight——redis的桌面UI工具使用实践

下载 官网下载安装。下载地址在这里 填个邮箱地址就可以下载了。 安装使用。 安装成功后开始使用。 1. 你可以add一个地址。或者登录redis cloud 去auto-discover 2 . 新增你的redis库地址。注意index的取值 3。现在可以登录到redis了。看看结果 这是现在 在服务器上执行…

AC修炼计划(AtCoder Beginner Contest 328)

传送门&#xff1a; Toyota Programming Contest 2023#7&#xff08;AtCoder Beginner Contest 328&#xff09; - AtCoder 本章对于自己的提升&#xff1a;dfs的运用&#xff0c;带权并查集&#xff0c;以及状压dp。 A&#xff0c;B&#xff0c;C题比较简单&#xff0c;直接…

虚拟机配置完NAT模式之后可以和主机ping通但是ping 百度显示:网络不可达

具体linux网络配置看这&#xff1a;http://t.csdnimg.cn/KRami 解决方案如下&#xff1a; 如果这里网关为空&#xff0c;那么和我遇到的问题一样网关没有设置上&#xff0c;在这直接配置网关之后重启即可

windows10关闭自动更新

windows10关闭自动更新 下载策略组改配置组配置 下载策略组 我自己的电脑里没有这个文件gpedit.msc所以要下载 pushd "%~dp0"dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum >List.txtdir /b C:\Windows…

机器学习算法项目开发流程

机器学习算法是当今人工智能领域最重要的技术之一&#xff0c;它可以让计算机通过学习数据中的模式和规律来实现预测和决策。在实际应用中&#xff0c;开发一个成功的机器学习算法项目需要遵循一定的开发流程。本文将介绍一个常见的机器学习算法项目开发流程&#xff0c;帮助读…

系统安全测试详解

一、前言 我刚开始接触安全测试的时候&#xff0c;想的最多就说那种在昏暗的灯光下&#xff0c;带着神秘面具的黑客&#xff0c;对着键盘噼里啪啦一顿猛如虎的操作&#xff0c;然后长舒一口气&#xff0c;最后来了句yes&#xff0c;完美收工&#xff01; 随后的职业生涯中&am…

Django学习日志08

如何开启事务 事务的目的&#xff1a;为了保证多个SQL语句执行成功&#xff0c;执行失败&#xff0c;前后保持一致&#xff0c;保证数据安全 ACID属性&#xff1a; A&#xff1a;原子性&#xff08;Atomicity&#xff09;&#xff1a;指事务是原子的&#xff0c;对事务中的操…

python数据可视化之matplotlib.pyplot

文章目录 模块引用折线条图实际应用案例关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 不论是数据挖掘还是数据…

Hadoop-- hdfs

1、HDFS中的三个进程&#xff1a;NameNode&#xff08;NN&#xff09;、DataNode(DN)、SecondNameNode(SNN) 2、NameNode&#xff08;NN&#xff09; 1、作用&#xff1a; 1、接收客户端的一个读、写的服务&#xff0c;在namenode上存储了数据文件和datanode的映射的关系。 …

移动云电脑:摆脱传统桎梏,助推企业数字化转型

如今&#xff0c;随着“云”在企业数字化转型战略中的作用日益凸显&#xff0c;上云是企业数字化转型第一步&#xff0c;已成为业界共识。尤其对于中小企业而言&#xff0c;数字化转型更是一种生存之道。 实际上&#xff0c;企业数字化转型面临很多传统桎梏。例如&#xff0c;数…

企业数字化转型的好处?_光点科技

企业数字化转型是当今商业世界中一个至关重要的议题。数字化转型不仅仅意味着采用新技术&#xff0c;而是涉及到企业在文化、运营和客户体验方面的根本变革。那么&#xff0c;企业数字化转型的好处是什么呢&#xff1f; 1.数字化转型可以显著提高企业的运营效率。 通过自动化流…

java-jdbc快速入门

文章目录 简介快速入门 简介 JDBC就是使用Java语言操作关系数据库的一套APIJava DataBase Connectivity 快速入门 -- mysql 中准备工作 create database if not exists my_db; use my_db; create table account(id int,name varchar(20),money int ); insert into account v…

深度学习之基于YoloV5血红细胞检测识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习已经在许多领域中得到了广泛的应用&#xff0c;包括医疗健康领域。其中&#xff0c;YOLO&#xff08;You O…

Python实现WOA智能鲸鱼优化算法优化随机森林回归模型(RandomForestRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 鲸鱼优化算法 (whale optimization algorithm,WOA)是 2016 年由澳大利亚格里菲斯大学的Mirjalili 等提…

国学---佛系算吉凶~

佛系算吉凶咯~&#xff0c;正经走访深山庙宇&#xff0c;前辈老人&#xff0c;经过调研后&#xff0c;搭建的轻衍计算模型&#xff0c;团队对国学的初次信息化尝试。 共享给有需要的朋友&#xff0c;准不准没关系&#xff0c;开心最重要。 后续还有财富&#xff0c;事业&…

2020年12月 Scratch(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 角色初始位置坐标是(0,0),执行下面程序后,角色会出现在什么位置上? A:x坐标为10,y坐标为50 B:x坐标为40,y坐标为50 C:x坐标为50,y坐标为40 D:x坐标为30,y坐标为50 答案…

解决Kibana初始化失败报错: Unable to connect to Elasticsearch

现象&#xff1a; 原因&#xff1a; docker run生成容器的时候&#xff0c;指定elastic server时指向了localhost 为什么不能是localhost, 因为这个localhost指向的是容器本身的网络&#xff0c;而elastic用的是物理网络&#xff0c;两个网络是隔离的&#xff0c;所以如果kiba…

有Mac或无Mac电脑通用的获取安卓公钥的方案

从2023年9月开始&#xff0c;所有上架应用市场的app都需要进行APP备案。 其中后端服务器在阿里云的可以在阿里云备案&#xff0c;后端服务器在腾讯云的可以在腾讯云备案。但无论你是在什么云厂商里做备案&#xff0c;无一例外的是&#xff0c;无论是上架安卓应用还是上架IOS应…

# Apifox前后端开发人员使用场景

Apifox前后端开发人员使用场景 概述 官网&#xff1a;Apifox 快速入门 | Apifox 帮助文档 功能描述 1、在日常编程开发过程中经常使用前后端分离架构的模式&#xff0c;一个项目的落地会通过产品、开发、测试三方会审&#xff0c;对项目需求评审过后&#xff0c;前后端开发会…