【数据结构】数组实现队列(详细版)
目录
队列的定义
普通顺序队列的劣势——与链队列相比
顺序队列实现方法:
一、动态增长队列
1、初始化队列
2、元素入队
3、判断队列是否为空
4、元素出队
5、获取队首元素
6、获取队尾元素
7、获取队列元素个数
8、销毁队列
总结:
动态增长队列完整测试代码:
二、固定长度队列
1、与动态增长队列的差异
2、判断是否队满
固定长度队列完整测试代码:
本节我们采用数组(顺序表)形式实现队列,学习单链表实现请点击下方链接:
队列—单链表实现(C语言版)-CSDN博客
为了减少数组初始长度过大对内存空间的浪费,本节我们采用动态内存管理,相关函数请的介绍点击下方链接:
动态内存函数(malloc,free,calloc,realloc)-CSDN博客
循环队列的实现:
循环队列(数组实现)-CSDN博客
队列的定义
队列是一种基本的数据结构,它是一种先进先出(First In First Out,FIFO)的线性结构。队列只允许在表的一端进行插入,而在另一端进行删除操作。这就相当于把数据排成一排,先插入的排在前面,后插入的排在后面,之后进行删除操作时也只能从前面依次删除。这种数据结构一般用于需要按照先后顺序进行处理的问题,如模拟系统、计算机网络中的缓存、操作系统中的进程调度等。队列的基本操作包括入队(插入元素到队尾)和出队(从队头删除元素),队列还有一个重要的特性就是队列的长度是动态变化的,随着入队和出队的操作进行不断变化。
普通顺序队列的劣势——与链队列相比
-
长度固定:普通数组队列的长度是固定的,一旦数组被分配,其长度无法改变。当队列元素数量超过数组长度时,需要进行数组的扩容操作,这会导致性能上的开销。
-
内存的浪费:因为普通数组队列的长度固定,可能会出现队列中存在空闲的位置,导致内存的浪费。
为了解决上述 问题1,我们在本节中对顺序表采取动态内存管理,在必要时更新数组的长度,以保证顺序队列的长度足够使用。
(补充:问题2 的解决需要使用循环队列,本节内容先为大家介绍一般队列的实现,等同学们对队列有了充分的理解之后,我们下节再进行循环队列的学习。)
顺序队列实现方法:
一、我们首先定义一个数组,数组的头部为队首,尾部为队尾。每当插入一个元素时,就将元素放在队尾,当删除一个元素时,将队首的元素删除。当队列为空时,不能再删除元素。
二、我们采用双指针法时刻记录队列的队首和队尾:
-
定义一个固定大小的数组作为队列的存储空间,并定义两个指针front和rear分别指向队列的队首和队尾。
-
初始化队列时,将front和rear都设置为0,表示队列为空。
-
插入元素时,将元素放入rear指针指向的位置,并将rear指针后移一位。
-
删除元素时,将front指针后移一位。
-
判断队列是否为空,只需要判断front和rear是否相等即可。
一、动态增长队列
1、初始化队列
初始化队列时,将front和rear都设置为0,表示队列为空。
typedef int DataType;typedef struct Queue
{DataType* a; // 队列的数组int front, rear; // 队列的头部和尾部位置索引int size; // 队列中元素的数量int capacity; // 队列的容量
} Queue;// 初始化队列
void InitQueue(Queue* q)
{q->a = NULL; // 数组指针初始化为NULLq->front = q->rear = 0; // 头部和尾部位置索引初始化为0q->size = q->capacity = 0; // 元素数量和容量都初始化为0
}
2、元素入队
// 入队
void QueuePush(Queue* q, DataType x)
{assert(q); // 断言q不为NULLif (q->capacity == q->rear){// 如果队列已满,进行扩容操作int new_capacity = q->capacity == 0 ? 10 : q->capacity * 2; // 扩容的大小为原容量的2倍DataType* temp = (DataType*)realloc(q->a, new_capacity * sizeof(DataType)); // 重新分配内存空间if (temp == NULL){perror("realloc fail"); // 扩容失败,则输出错误信息exit(-1); // 退出程序}q->capacity = new_capacity; // 更新队列的容量q->a = temp; // 更新数组指针}q->a[q->rear++] = x; // 在尾部插入新元素,并更新尾部位置索引q->size++; // 元素数量加1
}
3、判断队列是否为空
判断队列是否为空,只需要判断front和rear是否相等即可。
// 判断队列是否为空
bool QueueEmpty(Queue* q)
{assert(q); // 断言q不为NULLif (q->front == q->rear){return true; // 头部和尾部位置索引相等,队列为空}return false; // 队列不为空
}
4、元素出队
删除元素时,将front指针后移一位。
void QueuePop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){q->front++; // 更新头部位置索引q->size--; // 元素数量减1}else{printf("队列已空,删除失败!\n"); // 队列为空,无法出队}
}
5、获取队首元素
// 获取队首元素
DataType QueueTop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->front]; // 返回队首元素的值}else{printf("队列已空,获取队头元素失败!\n"); // 队列为空,无法获取队首元素exit(-1); // 退出程序}
}
6、获取队尾元素
队尾指针q->rear在最后一个元素的下一位,所以我们返回队尾元素时需要返回队尾坐标的前一个坐标所指向的元素。
// 获取队尾元素
DataType QueueTail(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->rear - 1]; // 返回队尾元素的值}else{printf("队列已空,获取队尾元素失败!\n"); // 队列为空,无法获取队尾元素exit(-1); // 退出程序}
}
7、获取队列元素个数
// 获取队列中元素的数量
int QueueSize(Queue* q)
{assert(q); // 断言q不为NULLreturn q->size; // 返回元素数量
}
8、销毁队列
// 销毁队列
void QueueDestory(Queue* q)
{assert(q); // 断言q不为NULLfree(q->a); // 释放队列的数组空间q->a = NULL; // 数组指针置为NULL
}
总结:
通过对顺序队列的学习我们可以明显看到顺序队列的缺点。当我们删除队首元素后,由于队列只能从队尾进行增加元素的操作,所以front指针之前的空间不能再进行使用。
如果是在队列长度是固定长度的情况下,当队尾指针rear到达最大时,队列已满,数组内已经没有空间进行插入操作,但由于此时front指针前可能还有空余空间,这时我们就造成了空间的浪费。
我们把这种现象称为“假溢出”现象。那么通过数组的循环队列或者链队列我们可以很好的解决此类现象。
下节我们将对如何用数组实现循环队列进行介绍:循环队列(数组实现)-CSDN博客
动态增长队列完整测试代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int DataType;typedef struct Queue
{DataType* a; // 队列的数组int front, rear; // 队列的头部和尾部位置索引int size; // 队列中元素的数量int capacity; // 队列的容量
} Queue;// 初始化队列
void InitQueue(Queue* q)
{q->a = NULL; // 数组指针初始化为NULLq->front = q->rear = 0; // 头部和尾部位置索引初始化为0q->size = q->capacity = 0; // 元素数量和容量都初始化为0
}// 判断队列是否为空
bool QueueEmpty(Queue* q)
{assert(q); // 断言q不为NULLif (q->front == q->rear){return true; // 头部和尾部位置索引相等,队列为空}return false; // 队列不为空
}// 入队
void QueuePush(Queue* q, DataType x)
{assert(q); // 断言q不为NULLif (q->capacity == q->rear){// 如果队列已满,进行扩容操作int new_capacity = q->capacity == 0 ? 10 : q->capacity * 2; // 扩容的大小为原容量的2倍DataType* temp = (DataType*)realloc(q->a, new_capacity * sizeof(DataType)); // 重新分配内存空间if (temp == NULL){perror("realloc fail"); // 扩容失败,则输出错误信息exit(-1); // 退出程序}q->capacity = new_capacity; // 更新队列的容量q->a = temp; // 更新数组指针}q->a[q->rear++] = x; // 在尾部插入新元素,并更新尾部位置索引q->size++; // 元素数量加1
}// 出队
void QueuePop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){q->front++; // 更新头部位置索引q->size--; // 元素数量减1}else{printf("队列已空,删除失败!\n"); // 队列为空,无法出队}
}// 获取队首元素
DataType QueueTop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->front]; // 返回队首元素的值}else{printf("队列已空,获取队头元素失败!\n"); // 队列为空,无法获取队首元素exit(-1); // 退出程序}
}// 获取队尾元素
DataType QueueTail(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->rear - 1]; // 返回队尾元素的值}else{printf("队列已空,获取队尾元素失败!\n"); // 队列为空,无法获取队尾元素exit(-1); // 退出程序}
}// 获取队列中元素的数量
int QueueSize(Queue* q)
{assert(q); // 断言q不为NULLreturn q->size; // 返回元素数量
}// 销毁队列
void QueueDestory(Queue* q)
{assert(q); // 断言q不为NULLfree(q->a); // 释放队列的数组空间q->a = NULL; // 数组指针置为NULL
}int main()
{Queue q;InitQueue(&q);QueuePush(&q, 5);QueuePush(&q, 6);QueuePush(&q, 7);QueuePush(&q, 8);QueuePush(&q, 9);QueuePush(&q, 10);DataType x;x = QueueTop(&q);printf("%d\n", x);x = QueueTail(&q);printf("%d\n", x);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);x = QueueTop(&q);printf("%d\n", x);x = QueueSize(&q);printf("%d\n", x);QueueDestory(&q);return 0;
}
二、固定长度队列
1、与动态增长队列的差异
由于固定长度队列无需扩容,所以不需要进行动态内存的分配,也不需要进行销毁队列的操作。
同时相对于动态增长的队列,固定长度的队列需要判断队内元素数量是否达到了队列的最大容量。由于我们在代码中是先对队尾指针rear指向的位置添加元素,再对rear进行自增,更新队尾索引,所以在本代码中队满的判断条件是rear==MAXLEN。
2、判断是否队满
当对固定长度队列添加元素时,如果当前队列队尾指针已达到数组长度,由于队列只能从队尾添加元素,此时我们不能再为队列添加新的元素。所以在我们为队尾添加元素时,我们首先要判断队列是否已满——即队尾指针是否达到数组容量的最大值。
//判断队列是否为满
bool QueueFull(Queue* q)
{assert(q); // 断言q不为NULLif (q->rear == MAXLEN){return true; // 尾部位置达到数组长度最大值,队列为满}return false; // 队列不为满
}
明白了以上几点,我们对动态增长队列的代码稍作修改,添加判断队列是否已满的函数并对增加队列元素作出限制,就可得到固定长度队列的代码。
固定长度队列完整测试代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>#define MAXLEN 10
typedef int DataType;typedef struct Queue
{DataType a[MAXLEN]; // 队列的数组int front, rear; // 队列的头部和尾部位置索引int size; // 队列中元素的数量
} Queue;// 初始化队列
void InitQueue(Queue* q)
{assert(q);q->front = q->rear = 0; // 头部和尾部位置索引初始化为0q->size = 0; // 元素数量初始化为0
}// 判断队列是否为空
bool QueueEmpty(Queue* q)
{assert(q); // 断言q不为NULLif (q->front == q->rear){return true; // 头部和尾部位置索引相等,队列为空}return false; // 队列不为空
}//判断队列是否为满
bool QueueFull(Queue* q)
{assert(q); // 断言q不为NULLif (q->rear == MAXLEN){return true; // 尾部位置达到数组长度最大值,队列为满}return false; // 队列不为满
}// 入队
void QueuePush(Queue* q, DataType x)
{assert(q); // 断言q不为NULLif (!QueueFull(q))//判断队列是否为满{q->a[q->rear++] = x; // 在尾部插入新元素,并更新尾部位置索引q->size++; // 元素数量加1}else{printf("队列已满\n");exit(-1);}
}// 出队
void QueuePop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){q->front++; // 更新头部位置索引q->size--; // 元素数量减1}else{printf("队列已空,删除失败!\n"); // 队列为空,无法出队}
}// 获取队首元素
DataType QueueTop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->front]; // 返回队首元素的值}else{printf("队列已空,获取队头元素失败!\n"); // 队列为空,无法获取队首元素exit(-1); // 退出程序}
}// 获取队尾元素
DataType QueueTail(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->rear - 1]; // 返回队尾元素的值}else{printf("队列已空,获取队尾元素失败!\n"); // 队列为空,无法获取队尾元素exit(-1); // 退出程序}
}// 获取队列中元素的数量
int QueueSize(Queue* q)
{assert(q); // 断言q不为NULLreturn q->size; // 返回元素数量
}int main()
{Queue q;InitQueue(&q);QueuePush(&q, 5);QueuePush(&q, 6);QueuePush(&q, 7);QueuePush(&q, 8);QueuePush(&q, 9);QueuePush(&q, 10);QueuePush(&q, 5);QueuePush(&q, 6);QueuePush(&q, 7);QueuePush(&q, 8);//QueuePush(&q, 9);//QueuePush(&q, 10);DataType x;x = QueueTop(&q);printf("%d\n", x);x = QueueTail(&q);printf("%d\n", x);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);x = QueueTop(&q);printf("%d\n", x);x = QueueSize(&q);printf("%d\n", x);return 0;
}
如果有同学在部分地方有疑惑,欢迎评论区讨论。
本节内容告一段落,我们下节博客见。
循环队列(数组实现)-CSDN博客
相关文章:

【数据结构】数组实现队列(详细版)
目录 队列的定义 普通顺序队列的劣势——与链队列相比 顺序队列实现方法: 一、动态增长队列 1、初始化队列 2、元素入队 3、判断队列是否为空 4、元素出队 5、获取队首元素 6、获取队尾元素 7、获取队列元素个数 8、销毁队列 总结: 动态增长队列…...

Sharding-JDBC快速使用【笔记】
1 引言 最近在使用Sharding-JDBC实现项目中数据分片、读写分离需求,参考官方文档(Sharding官方文档)感觉内容庞杂不够有条理,重复内容比较多;现结合项目应用整理笔记如下供大家参考和自己回忆使用; 在…...

总结MySQL 的一些知识点:MySQL 排序
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
Linux中经常使用的相关命令
查看硬盘存储容量使用情况: df -lh 列出 /bin 目录中的 5 个最大文件: ls -lSh /bin | head -5 删除文件和文件夹 在Linux中,要删除文件的命令是rm。你可以使用以下命令来删除一个文件: rm file_name如果要删除多个文件,可…...
2022-2023年度广东省职业院校学生专业技能大赛“软件测试”赛项性能测试题目-Jmeter
性能测试-JM 1、脚本添加: 脚本文件名称:SuppAndComp,测试计划名称:SuppAndComp。测试计划下添加两个线程组: (1)线程组一操作内容:系统管理员登录、进行新增供应商操作。 线程组名称SuppAdd。具体要求如下: 登录操作存放到仅一次控制器中,供应商名称前4位为固定…...

R304S 指纹识别模块的硬件接口说明
一.外部接口尺寸图 二.串行通讯 R304S 指纹模块通讯接口定义: 引脚号名称定义描述15V电源输入电源正输入端 DC 4.2--6V2GND电源和信号地电源和信号地3TXD数据发送串行数据输出,TTL 逻辑电平4RXD数据接收串行数据输入,TTL 逻辑电平 三.USB通…...

postman使用-05新建测试集
文章目录 两种方式新建测试集测试集:允许用户以项目或模块的方式对多个接口进行分类和管理。每一个测试请求都可以被看作是一个独立的测试用例,而collections则可以同时管理多个测试用例的执行。方法一:点击左上角直接创建测试方法二…...
oracle 子查询和窗口函数
描述:给定一张学生学科成绩表base_student_grade,字段id表示学生学号,name为姓名,subject表示学科,grade为某学科成绩。使用子查询和窗口函数查询每个学生成绩最高的学科和分数。 select id,name,subject,grade from (select id,…...
数据库开发与设计过程中的问题分析总结
数据库设计的过程是将数据库系统与现实世界密切地、有机地、协调一致地结合起来的过程。数据库的设计质量与设计者的知识、经验和水平密切相关。作为数据库应用系统的重要组成部分,数据库设计的成败往往直接关系到整个应用系统的成败。以数据库为基础的数据库应用系…...

《数据库开发实践》之存储过程【知识点罗列+例题演练】
一、什么是存储过程? 1.概念理解: 存储过程是一组为了完成特定功能的SQL语句集。通过组成SQL语句和控制语句,提供一种封装任务的方法。因此在创建编译好某个存储过程后,因为存储过程中有可执行操作的sql语句,用户可以…...

Linux进程地址空间
🎬慕斯主页:修仙—别有洞天 ♈️今日夜电波:HEART BEAT—YOASOBI 2:20━━━━━━️💟──────── 5:35 🔄 ◀️ ⏸ ▶️ ☰ …...

2024.1.3 关于 Redis 渐进式遍历 和 数据库管理命令
目录 引言 渐进式遍历 SCAN 命令 数据库管理命令 切换数据库 获取数据库 key 个数 删除数据库所有 key 同步删除 SYNC 异步删除 ASYNC 阅读下述文章之前建议点击下方链接熟悉 keys 命令的用法和特点 Redis 全局通用命令 渐进式遍历 keys * 命令一次性将 Redi…...
并发编程:线程同步基础:5、读写锁。ReentrantReadWriteLock
1、主要方法 .readLock().lock();获取读锁 读锁之间互不干扰。 .writeLock().lock();获取写锁 写锁可以锁定住读锁和其他写操作。 2、主程序 package xyz.jangle.thread.test.n2_5.rwlock;import java.util.concurrent.TimeUnit;/*** * 读写锁。ReentrantReadWriteLock* a…...
SpringBoot 集成 Kafka消息中间件,Docker安装Kafka环境
前述 提供kafka、zooker在docker环境下进行安装的示例,springBoot集成kafka实现producer-生产者和consumer-消费者(监听消费:single模式和batch模式)的功能实现 环境安装 # 拉取镜像 docker pull wurstmeister/zookeeper docker pull wurstmeister/kafka# 运行zooker docker …...
阿里云Alibaba Cloud Linux 3镜像版本大全特性说明
Alibaba Cloud Linux阿里云打造的Linux服务器操作系统发行版,Alibaba Cloud Linux完全兼容完全兼容CentOS/RHEL生态和操作方式,目前已经推出Alibaba Cloud Linux 3,阿里云百科aliyunbaike.com分享Alibaba Cloud Linux 3版本特性说明ÿ…...

基于SSM的滁艺咖啡在线销售系统设计与实现
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…...
【设计模式之美】理论一:怎么才算是单一原则、如何取舍单一原则
文章目录 一. 如何判断类的职责是否足够单一?二. 类的职责是否设计得越单一越好? 开始学习一些经典的设计原则,其中包括,SOLID、KISS、YAGNI、DRY、LOD 等。 本文主要学习单一职责原则的相关内容。 单一职责原则的定义:…...

MYSQL 深入探索系列六 SQL执行计划
概述 好久不见了,近期一直在忙项目的事,才有时间写博客,近期频繁出现sql问题,今天正好不忙咱们看看千万级别的表到底该如何优化sql。 案例 近期有个小伙伴生产环境收到了告警,有个6千万的日志表,查询耗时大…...
安装jupyter notebook,jupyter notebook的简单使用
借助anaconda安装jupyter notebook,先下载anaconda然后在Anaconda Prompt中输入命令: 输入"jupyter notebook",在默认浏览器中打开jupyter notebook。 输入"jupyter notebook --no-browser",启动服务器,但不打…...

宏集PC Runtime软件助推食品行业生产线数字化革新
一、前言 近年来,中国食品行业发展迅速且灵活多变,在当前经济下行的情形下,食品行业正面临着日益激烈的竞争,导致企业利润下降。 为了保持企业市场竞争力,国内某top10食品企业采用宏集SCADA解决方案—PC Runtime软件…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...