【C++】一文简练总结【多态】及其底层原理&具体应用(21)

前言

大家好吖,欢迎来到 YY 滴C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁
主要内容含:
在这里插入图片描述

欢迎订阅 YY滴C++专栏!更多干货持续更新!以下是传送门!

目录

  • 一.多态的概念
  • 二.多态的实现
    • 1)虚函数&虚函数表
    • 2)虚函数的重写(覆盖)
    • 3)多态的构成条件
    • 4)虚函数重写的两种特殊情况:
        • 【1】协变:(基类与派生类虚函数返回值类型不同)
        • 【2】析构函数的重写:(基类与派生类析构函数的名字不同)
  • 三.【override】【final】关键字——帮助用户检测是否重写(C++11)
      • 【1】 final:表示虚函数不能被重写,被重写即报错
      • 【2】override:检查虚函数是否重写了别的虚函数,重写了即报错
  • 四. 多态的具体应用:抽象类(接口类)(纯虚函数类)
      • 1)利用 [ 只有重写纯虚函数 派生类才能实例化出对象 ] 性质
      • 2)实现继承与接口继承

一.多态的概念

  • 多态是在不同继承关系的类对象,去调用 同一 函数,产生了 不同 的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。
  • 例:iphone和安卓手机用户打车同程不同价

二.多态的实现

1)虚函数&虚函数表

  • 虚函数:即被 virtual 修饰的类成员函数称为虚函数。
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
  • 虚函数表本质是一个存虚函数指针 指针数组,一般情况这个数组最后面放了一个nullptr。
  • 虚函数表:虚函数表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚函数表中。
  • 一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表

2)虚函数的重写(覆盖)

  • 虚函数的重写(覆盖): 派生类中有一个跟基类完全相同的虚函数 (即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同) ,称子类的虚函数 重写 了基类的虚函数。

3)多态的构成条件

  1. 必须通过 基类的指针 引用 调用虚函数
  2. 被调用的函数 必须是虚函数,且 派生类必须对基类的虚函数进行重写
                                           //多态条件2:被调用的函数 必须是虚函数
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:                                   //多态条件2:派生类必须对基类的虚函数进行重写virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因
为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议
这样使用*/
/*void BuyTicket() { cout << "买票-半价" << endl; }*/
};void Func(Person& p)       //多态条件1:必须通过基类的指针来“引用”调用虚函数
{ 
p.BuyTicket(); 
}int main()
{
Person ps;
Student st;
Func(ps);
Func(st);return 0;
}

4)虚函数重写的两种特殊情况:

【1】协变:(基类与派生类虚函数返回值类型不同)
  • 派生类重写基类虚函数时 ,与基类虚函数 返回值类型不同 。即如下代码所示:【基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时】,称为协变
class A{};
class B : public A {};
class Person {
public:virtual A* f() {return new A;}
};
class Student : public Person {
public:virtual B* f() {return new B;}
};
【2】析构函数的重写:(基类与派生类析构函数的名字不同)
  • 如果 基类的析构函数为虚函数 ,此时派生类析构函数只要定义, 无论是否加virtual关键字
    都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。 虽然函数名不相同【~Person() 】 【~Student() 】,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成 destructor
class Person {
public:                  //基类的析构函数为虚函数virtual ~Person() {cout << "~Person()" << endl;}
};class Student : public Person {
public://~Student() { cout << "~Student()" << endl; 不加virtual也行virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,
//才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}

三.【override】【final】关键字——帮助用户检测是否重写(C++11)

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

【1】 final:表示虚函数不能被重写,被重写即报错

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

【2】override:检查虚函数是否重写了别的虚函数,重写了即报错

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

四. 多态的具体应用:抽象类(接口类)(纯虚函数类)

1)利用 [ 只有重写纯虚函数 派生类才能实例化出对象 ] 性质

  • 在虚函数的后面写上 =0 ,则这个函数为 纯虚函数 包含纯虚函数的类 叫做 抽象类(也叫接口类) [ 抽象类不能实例化出对象 ]&[ 派生类继承后也不能实例化出对象 ] 只有 [ 重写纯虚函数 ] ,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class Car
{
public:
virtual void Drive() = 0;         //在虚函数的后面写上 =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();
}

2)实现继承与接口继承

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

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

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

相关文章

SSM项目与Redis整合以及Redis注解式开发以及Redis击穿穿透雪崩

目录 前言 一、SSM项目整合Redis 1.导入pom依赖 2.Spring-redis相关配置 3.Spring上下文配置 二、redis注解式缓存 1.Cacheable 注解 2.CachePut 注解 3.CacheEvict 注解 三、redis击穿、穿透、雪崩 1. 缓存击穿 2. 缓存穿透 3. 缓存雪崩 前言 当将SSM项目与Red…

NSSCTF逆向题解

[SWPUCTF 2021 新生赛]简简单单的逻辑 直接把key打印出来&#xff0c;然后整理一下result&#xff0c;让key和result进行异或 key[242,168,247,147,87,203,51,248,17,69,162,120,196,150,193,154,145,8] data[0xbc,0xfb,0xa4,0xd0,0x03,0x8d,0x48,0xbd,0x4b,0x00,0xf8,0x27,0x…

uniapp中picker 获取时间组件如何把年月日改成年月日默认时分秒为00:00:00

如图所示&#xff0c;uniapp中picker组件的日期格式为&#xff1a; 但后端要 2023-11-08 00:00:00格式 如何从2023-11-08转化为 2023-11-08 00:00:00&#xff1a;&#x1f447; const date new Date(e.detail.value);//"2023-11-17" date.setHours(0, 0, 0); // 2…

docker部署MinIo

部署单个的MinIo 要在Docker中安装Minio&#xff0c;您可以按照以下步骤进行操作&#xff1a; 使用Docker命令拉取Minio镜像&#xff1a; docker pull minio/minio创建一个用于存储数据的本地目录。例如&#xff1a; mkdir -p /minio/path/to/data运行Minio容器&#xff1a…

[Linux/UOS]同一解决方案下的控制台程序依赖SO库的方法

该方法是基于VS2019的远程调试Linux的方案&#xff0c;使用的是UOS系统&#xff0c;本文不会去详述如何远程调试Linux和如何新建解决方案中的.so项目和.out项目 只关注于如何令.out项目依赖.so&#xff0c;并成功调用运行 以一个如上图结构的解决方案为例子&#xff0c;SysInfo…

合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(一)

基于ARM语音识别的智能家居系统 我们接下来带大家完成基于语音识别的智能家居系统嵌入式项目实战&#xff0c;使用到stm32开发板&#xff0c;讯飞的离线语音识别&#xff0c;我们在此之前&#xff0c;我们先学习一些Linux系统的基本操作。 。 一、Linux简介 在嵌入式开发中&am…

Linux开发工具的使用(vim、gcc/g++ 、make/makefile)

文章目录 一 &#xff1a;vim1:vim基本概念2:vim的常用三种模式3:vim三种模式的相互转换4:vim命令模式下的命令集- 移动光标-删除文字-剪切/删除-复制-替换-撤销和恢复-跳转至指定行 5:vim底行模式下的命令集 二:gcc/g1:gcc/g的作用2:gcc/g的语法3:预处理4:编译5:汇编6:链接7:函…

数字通信和fpga概述——杜勇版本学习笔记

1数字通信处理流程 脉冲调制是每个数字通信系统中间必不可少的环节&#xff0c;通常是使用升余弦滚降滤波器来实现。 超外差接收机原理是利用本地产生的振荡波与输入信号混频&#xff0c;将输入信号频率变换为某个预先确定的频率的方法。超外差原理最早是由E.H.阿姆斯特朗于1…

【理解链表指针赋值】链表中cur->next = cur->next->next->next与cur =cur->next->next的区别

最近在做链表的题目的时候&#xff0c;对于所定义的cur链表指针产生了一些疑惑&#xff0c;查阅资料后整理一下我的理解&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(n…

React中组件之间如何通信?

一、是什么 我们将组件间通信可以拆分为两个词&#xff1a; 组件通信 回顾Vue系列的文章&#xff0c;组件是vue中最强大的功能之一&#xff0c;同样组件化是React的核心思想 相比vue&#xff0c;React的组件更加灵活和多样&#xff0c;按照不同的方式可以分成很多类型的组件…

SQL注入漏洞 其他注入

文章目录 宽字节注入案例 HTTP头部注入Cookie注入base64User-Agent注入Referer 注入 SQL注入读写文件条件1.是否拥有读写权限2.文件路径3.secure_file_priv 读取文件写入文件 SQLMap安装sqlmapkail 源安装仓库克隆 参数简介快速入门&#xff1b;SQLmap&#xff08;常规&#xf…

Vim编辑器学习

B站学习vim指令链接 1&#xff1a;vim下有两种模式&#xff0c;一种是命令模式&#xff0c;一种是编辑模式 2&#xff1a;命令到编辑模式&#xff0c;按键盘i&#xff0c;编辑到命令格式按Esc 3&#xff1a;&#xff1a;wq 保存并退出 &#xff1a;wq code.c保存并把文件命名为…

unity Holoens2开发,使用Vuforia识别实体或图片 触发交互

建议&#xff1a;先看官方文档 我使用的utniy 版本&#xff1a;Unity 2021.3.6f1 官方建议&#xff1a;混合现实工具包简介 - 设置项目并使用手势交互 - Training | Microsoft Learn 配置了正确工具的 Windows 10 或 11 电脑Windows 10 SDK 10.0.18362.0 或更高版本安装了 U…

Redis实战 | 使用Redis 的有序集合(Sorted Set)实现排行榜功能,和Spring Boot集成

专栏集锦&#xff0c;大佬们可以收藏以备不时之需 Spring Cloud实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏&#xff1a;https:/…

网络通信——与Socket交换数据(三十一)

1. 与Socket交换数据 1.1 知识点 &#xff08;1&#xff09;通过Android与Socket完成基本的Echo程序实现&#xff1b; &#xff08;2&#xff09;通过对象序列化进行大数据的传输&#xff1b; 1.2 具体内容 对于网络的开发而言&#xff0c;最常使用的交互模式&#xff1a;W…

算法之路(一)

&#x1f58a;作者 : D. Star. &#x1f4d8;专栏 :算法小能手 &#x1f606;今日分享 : 如何学习&#xff1f; 在学习的过程中&#xff0c;不仅要知道如何学习&#xff0c;还要知道避免学习的陷阱。1. 睡眠不足&#xff1b;2. 被动学习和重读&#xff1b;3. 强调标记或画线&am…

基于单片机的甲醛检测器设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、系统硬件设计三、软件设计4.1 程序结构流程图原理图 四、结论五、 文章目录 概要 本文将要提…

【k8s】pod控制器

一、pod控制器及其功用 Pod是kubernetes的最小管理单元&#xff0c;在kubernetes中&#xff0c;按照Pod的创建方式可以将其分为两类 自主式Pod&#xff1a; kubernetes直接创建出来的Pod&#xff0c;这种Pod删除后就没有了&#xff0c;也不会重建 控制器创建的Pod&#xff1a…

2023.11.7 Spring 依赖注入的三大方式

目录 前言 属性注入&#xff08;Autowired&#xff09; Setter 注入 构造方法注入 Resource Autowired 和 Resource 的区别 Autowired 和 Resource 查找 Bean 对象的区别 前言 配置文件 ​ <?xml version"1.0" encoding"UTF-8"?> <beans …

uniapp在不需要后端数据的情况下 怎么记录用户进一次记录一次

目录 前言&#xff1a; html部分 js部分 完整代码 前言&#xff1a; 一时兴起&#xff0c;不喜勿喷&#xff0c;今天听到了这个问题想到了一个方法&#xff0c;解决方式如下。 html部分 他用于显示访问次数&#xff08;visitCount变量的值&#xff09;。 <template&…