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.rhkb.cn/news/252106.html

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

相关文章

Python算法题集_环形链表

Python算法题集_环形链表 题234&#xff1a;环形链表1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【集合检索】2) 改进版一【字典检测】3) 改进版二【双指针】 4. 最优算法 本文为Python算法题集之一的代码示例 题234&#xff1a;环形链表 …

【Elasticsearch】从入门到精通

目前java常见的针对大数据存储的方案并不多&#xff0c;常见的就是mysql的分库分表、es存储 这里偏向es存储方案&#xff0c;es不同的版本之间其实差异还挺大的&#xff0c;本篇博文版本Elasticsearch 7.14.0 Springboot整合Easy-Es Easy-Es官方文档 Elasticsearch的初步认识 …

自学Java的第十九天

一&#xff0c;每日收获 1.排序 2.冒泡排序法 3.查找 4.多维数组-二维数组 二&#xff0c;新名词与小技巧 三&#xff0c;今天学习中所遇到的困难 一&#xff0c;每日收获 1.排序 ① 排序的介绍 排序是将多个数据&#xff0c;依指定的顺序进行排列的过程。 ② 排序的…

【Rust】——rust前言与安装rust

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

使用 PyTorch 构建 NLP 聊天机器人

一、说明 聊天机器人提供自动对话&#xff0c;可以帮助用户完成任务或寻求信息。随着深度学习的最新进展&#xff0c;聊天机器人正变得越来越具有对话性和实用性。这个全面的教程将利用 PyTorch 和 Python 从头开始构建聊天机器人&#xff0c;涵盖模型架构、数据准备、训练循环…

认识Tomcat (一)

认识Tomcat &#xff08;一&#xff09; 一、服务器 1.1 服务器简介 ​ 硬件服务器的构成与一般的PC比较相似&#xff0c;但是服务器在稳定性、安全性、性能等方面都要求更高&#xff0c;因为CPU、芯片组、内存、磁盘系统、网络等硬件和普通PC有所不同。 ​ 软件服务器&…

初始数据库

华子目录 什么是数据库DBMS&#xff08;数据库管理系统&#xff09;数据库系统和文件系统的区别文件系统数据库系统对比区别优缺点总结 常见数据库关系型数据库ACID原则关系型数据库的优缺点优点缺点 非关系型数据库存储方式常见的非关系型数据库非关系型数据库的优缺点优点缺点…

BUU UPLOAD COURSE 1

进去之后是一个上传页面 尝试上传一句话木马的php代码&#xff0c;保存为一个1.php&#xff0c;然后上传 <?php eval($_POST[1]);?>发现后缀名被改为jpg了 访问一下 http://a82bcc09-b809-42c9-b5ad-5406b72e5707.node5.buuoj.cn:81/uploads/65bfa77eab1f6.jpg发现可…

代码生成器(新):mybatis-plus-generator使用指南

代码生成器&#xff08;新&#xff09;官网 后端代码&#xff1a;点击查看 LearnElementUiAndSpringBoot 提醒&#xff1a;LearnElementUiAndSpringBoot下载完后&#xff0c;在运行调试 Main.java里的main方法之前&#xff0c;除了utils包和Main.java文件&#xff0c;其他包需…

异地办公必不可缺的远程控制软件,原理到底是什么?

目录 引言远程桌面连接软件的作用与重要性 基本概念与架构客户端-服务器模型网络通信协议 核心技术组件图形界面捕获与传输输入转发会话管理 性能优化策略带宽优化延迟优化 引言 远程桌面连接软件的作用与重要性 在当今这个高度数字化和网络化的时代&#xff0c;远程桌面连接软…

红队渗透靶机:LORD OF THE ROOT: 1.0.1

目录 信息收集 1、arp 2、nmap 3、knock 4、nikto 目录探测 1、gobuster 2、dirsearch WEB sqlmap 爆库 爆表 爆列 爆字段 hydra爆破 ssh登录 提权 信息收集 内核提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, ty…

U形金属卡

方管不锈钢防盗网挂勾&#xff0c;没找到合适的&#xff0c;自己设计一个。 difference(){ cube([43,20,27]);translate([2,-1,2]) cube([42,29,23]);translate([32,10,-1]) cylinder(50,3,3,$fn360); }if(1){ color("gray") translate([2,-20,2]) cube([23,60,23])…

JAVA中的object类

一、equals方法 1.和equals的比较 既可以判断基本类型有可以判断引用类型 如果判断基本类型&#xff0c;判断的值是否相等 int num1 10; double num2 10.0; System.out.println(num1 num2); 如果判断引用类型&#xff0c;判断的地址是否相等&#xff0c;即判断是不是同一…

myql 项目数据库和表的设计

1.表的设计和创建 2.在navicate运行这些代码 create table user(id int not null auto_increment primary key,name varchar(50) not null unique,password varchar(50) not null,state enum(online,offline) default offline ); create table friend(userid int not null,…

HomeAssistant系统添加HACS插件商店与远程控制家中智能家居

文章目录 基本条件一、下载HACS源码二、添加HACS集成三、绑定米家设备 ​ 上文介绍了如何实现群晖Docker部署HomeAssistant&#xff0c;通过内网穿透在户外控制家庭中枢。本文将介绍如何安装HACS插件商店&#xff0c;将米家&#xff0c;果家设备接入 Home Assistant。 基本条件…

【初中生讲机器学习】5. 从概率到朴素贝叶斯算法,一篇带你看明白!

创建时间&#xff1a;2024-02-04 最后编辑时间&#xff1a;2024-02-05 作者&#xff1a;Geeker_LStar 你好呀~这里是 Geeker_LStar 的人工智能学习专栏&#xff0c;很高兴遇见你~ 我是 Geeker_LStar&#xff0c;一名初三学生&#xff0c;热爱计算机和数学&#xff0c;我们一起加…

[C++]继承(续)

一、基类和派生类对象赋值转换 在public继承时&#xff0c;父类和子类是一个“is - a”的关系。 子类对象赋值给父类对象/父类指针/父类引用&#xff0c;我们认为是天然的&#xff0c;中间不产生临时对象&#xff0c;也叫作父子类赋值兼容规则&#xff08;切割/切片&#xff…

工作与生活平衡:在生活中寻找和谐

工作和生活是我们生活中不断交织的两个重要方面。对许多人来说&#xff0c;找到两者之间的完美平衡已经成为一个持久的挑战。然而&#xff0c;与其专注于平衡&#xff0c;更重要的是要认识到工作和生活并不是可以相互平衡的两个分离实体&#xff0c;而是一个相互影响的循环。正…

服务器和云服务器哪个更安全?

随着云计算技术的不断发展&#xff0c;越来越多的企业开始选择使用云服务器来存储和处理数据。然而&#xff0c;对于一些企业来说&#xff0c;他们可能更倾向于使用传统的服务器。在这种情况下&#xff0c;安全性成为了一个重要的考虑因素。那么&#xff0c;服务器和云服务器哪…

普通编程,机器学习与深度学习

普通编程&#xff1a;基于人手动设置规则&#xff0c;由输入产生输出经典机器学习&#xff1a;人手工指定需要的特征&#xff0c;通过一些数学原理对特征与输出的匹配模式进行学习&#xff0c;也就是更新相应的参数&#xff0c;从而使数学表达式能够更好的根据给定的特征得到准…