C++当中的多态(一)

  (一)什么是多态

        1.现实中的多态:

  所谓的多态在我们的生活当中其实很常见。举一个简单的例子:当我们需要买票的时候有很多种不同的票可以供我们购买,如果你是学生就可以享受半价票的优惠,如果你是VIP用户就可以享受八五折的优惠,如果你是烈士家属就可以享受优先购票的权利.......那么像这种针对于同一个窗口,但是由不同的人,来进行相同的购票操作,最终得到不同的结果的现象就叫做多态。

        2.C++当中的多态

  在C++当中多态一般发生在继承的过程当中。如果我们想要实现我们上述说明的功能就需要使用C++当中有关于多态的语法。设置一个虚函数,之后对函数进行重写,使得我们不同的对象在调用相同的虚函数的时候可以输出不同的结果。C++当中的多态效果如下:

  我们会发现通过上述的代码我们已经实现了我们在显示当中所描述的场景,但是大家肯定对上述的代码有很多不理解的地方,不要着急,慢慢学习即可。

  (二)多态的定义及实现

  多态实际上就是不同继承关系的类对象,在调用同一个函数,做出了不同行为的现象。比如我们上面编写的代码当中学生类以及VIP类等都继承了购票窗口类。之后又在本身的类当中重新编写了一个同名的函数:购票函数。之后通过调用就实现了多态。

  想要构成多态我们需要满足两个条件:
        1、存在虚函数,并且在派生类当中对虚函数进行重写操作
        2、通过基类的指针或者引用进行调用
  (1)虚函数

  那么什么是虚函数呢?所谓的虚函数其实很简单,就是在我们需要的函数前面加上virtual关键字即可。加上virtual关键字的函数就会被认定为虚函数。需要注意的是:只有在继承当中才存在虚函数的概念。

  (2)虚函数的重写

  表明虚函数之后就要对虚函数进行重写操作。首先虚函数的重写需要满足三同。即函数名相同,返回值相同,参数类型相同。也就是说设置一个和原函数的列表一模一样的函数进行重新的编写,只有函数体不相同的操作就叫做函数的重写。

  回到我们刚才编写的代码进行观察:

  我们会发现,我们编写好的派生函数完全满足我们函数重写的概念。

  但是难道只有三同的时候才可以构成虚函数的重写吗?其实对于多态来说有一个例外,那就是协变,在协变当中可以允许我们重写的函数的返回值不同,但是必须要求我们的返回值是基类和派生类的指针,基类对应基类,派生类对应派生类。例如我们可以将上述代码修改成为以下形式:

  我们会发现,修改完成之后我们的代码依旧可以正常的运行,在协变当中可以作为返回值的基类和父类指针并不一定要求是我们本身的类的指针也可以是其他的基类和派生类的关系。例如:

  我们会发现我们也可以返回其他的基类跟派生类的关系,但是需要注意的是:基类必须对应基类,派生类必须对应派生类。这种用法在实际的使用当中并不多见,所以我们了解即可。

  1.特殊存在:析构函数的多态

  大家觉得我们的析构函数可以进行重写吗?我们来思考一下:我们的析构函数格式为:

  ~类名()由于我们基类跟派生类的名字不同,所以我们的析构函数的命名也不相同,那么岂不是构不成函数重写的形式了?那么我们再来分析一下:我们的析构函数需要进行重写吗?有一个最简单的例子进行证明:

  我们会发现通过基类的指针调用的时候可以正常的调用我们已经完成重写的虚函数,但是我们的析构函数没有经过重写,所以只会调用我们基类当中的析构函数。但是我们又会发现,就像是我们已经修改过的学生的类当中展示的那样:我们的派生类当中也可能会有新的从堆上开辟的空间。那么我们就需要调用我们student类当中的析构函数,进行该部分空间的释放,仅仅调用基类当中的析构函数显然是无法满足我们的要求的。所以我们得到一个结果:析构函数也需要进行重写。但是对于析构函数怎么进行重写呢?

  不要担心,在编译器当中我们的析构函数已经被封装成为一个函数名均为destroy的函数。这样我们就可以进行析构函数的重写了。所以我们可以将代码修改成为如下的形式:

  我们会发现程序运行正常了,当我们删除student的时候会自动调用student当中的析构函数。但是我们又会发现一个很奇怪的现象:那么就是为什么最后的析构函数调用的很奇怪呢?

  2.析构函数调用的顺序

  这个就涉及到了我们析构函数调用的顺序,重新梳理一下我们继承的构造流程。在使用派生类对基类进行继承之后。创建一个派生类的对象会优先调用我们基类当中的构造函数进行初始化,之后再调用派生类当中的构造函数进行初始化。这样可以避免在派生类的构造函数当中可能会有需要使用基类成员的情况,没有进行初始化我们就无法正常的使用基类成员。

  那么反观我们的析构函数。首先我们是先调用基类的构造函数,再调用派生类的构造函数。由于函数调用是一个栈结构,所以我们会优先调用派生类的析构函数进行释放空间,之后再调用基类的析构函数进行释放空间,符合我们析构函数调用的顺序。其次,我们再派生类的析构函数当中依旧可能使用到基类当中的成员,所以我们先进行析构派生类也符合我们正常的使用需求。 

  那么肯定会有人有疑问了:派生类释放一次空间,基类再释放一次空间难道就不怕空间的重复释放吗?其实不需要担心这个问题,因为我们在派生类当中进行析构的时候进行的操作仅仅释放了我们在派生类当中开辟的空间,而我们调用基类的析构函数释放的就是我们在基类当中开辟的空间,两个空间完全不相同,所以不需要担心空间的重复释放的问题出现。

  (3)为什么要使用基类指针或引用进行调用重写的虚函数

  我们可以将问题拆分成为两个部分:为什么要使用指针或引用进行调用重写的虚函数,以及为什么一定要用基类的指针或引用才可以调用我们重写的虚函数呢?这样又有什么好处呢?我们来一一进行分析。

  1.为什么要使用基类进行调用虚函数

  记得我们之前学继承的时候说到的派生类跟基类之间的相互转换吗?在两个数据类型进行转换的时候我们可以将派生类当中的数据进行切割得到一个基类的对象,完成赋值操作,但是我们却无法使用基类对派生类进行赋值,因为对于我们派生类多出来的部分我们不能够得到明确的数据。想到这点其实就很容易理解了。

  使用基类进行虚函数的调用无非就是为了让我们各个类型的对象都可以成功的调用我们的虚函数,无论是派生类还是基类都可以以直接或间接的方式进行访问最终达到我们想要的效果。

  2.为什么要使用指针或引用进行调用虚函数

  那么为什么要使用指针或引用进行调用虚函数呢?这就涉及到我们比较底层的一些只是了。实际上在将我们基类当中的函数设置成为虚函数的时候,会自动生成一张虚函数表。在虚函数表当中保存了我们所有设置成为虚函数的地址。经过继承之后我们的虚函数表也会被派生类所继承。如果派生类对我们的虚函数进行了重写的操作,那么就会有一个新的函数地址。我们使用这个新的函数地址将原有函数表当中的地址进行覆盖就成了一张新的虚函数表。

  如果使用对象作为参数的话,在切割的时候并不会将虚函数表进行复制给我们的基类,因为基类当中的虚函数并不改变。但是如果我们使用引用或指针作为参数传参的话,那么我们实际上使用的还是我们派生类当中的虚函数表。(就好比我们使用int类型数据的指针或引用进行传参的时候,所使用的数据完全是我们本身一样)那么我们使用的就是经过修改之后的虚函数表了。自然查找的也就是我们经过重写之后的虚函数。这也就是我们使用引用或指针作为调用虚函数的参数的原因。

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

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

相关文章

Leetcode 只出现一次的元素

题目要求我们找到数组中只出现了一次的元素,而其他元素都出现了两次。 解题思路: 我们可以使用位运算中的异或操作(XOR)。异或操作有以下两个特性: 相同的两个数字异或结果为0,例如:a ^ a 0…

Android12——Launcher3文件夹布局修改调整

文章声明:本文是笔者参考良心大佬作品后结合实际需求进行相应的定制,本篇主要是笔者记录一次解析bug笔记,文中可能会引用大佬文章中的部分图片在此声明,并非盈利目的,如涉嫌侵权请私信,谢谢! 大…

基于亲和性的 GPU 容器绑核策略 Copy

1.引言 在高性能计算和大规模并行任务处理中,GPU已经成为不可或缺的加速器。为了充分发挥GPU的计算能力,通过合理分配CPU核与GPU的绑定来优化CPU和GPU的关系至关重要。我们将探讨socket和NUMA(非统一内存访问)的概念,并…

力扣 — — 2555. 两个线段获得的最多奖品

力扣 — — 2555. 两个线段获得的最多奖品 一、题目描述 题目大意:给定一个数组prizePositions,数组中的值表示的是奖品的位置,每一个位置可以有多个奖品,并且设定一个线段的长度 K K K,要求从所有奖品位置中选择两个…

springboot 整合quartz定时任务

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、pom的配置1.加注解二、使用方法1.工程图2.创建工具类三、controller 实现前言 提示:这里可以添加本文要记录的大概内容: 提示:以下是本篇文章正文内容,下面案例可供参考 一、pom的配…

【RabbitMQ】工作模式

工作模式概述 简单模式 简单模式中只存在一个生产者,只存在一个消费者。生产者生产消息,消费者消费消息。消息只能被消费一次,也称为点对点模式。 简单模式适合在消息只能被单个消费者处理的场景下存在。 工作队列模式(Work Qu…

Redisson分布式锁实现及原理详解

随着技术快速发展,数据规模增大,分布式系统越来越普及,一个应用往往会部署在多台机器上(多节点),在有些场景中,为了保证数据不重复,要求在同一时刻,同一任务只在一个节点…

浏览器中的JavaScript核心BOM(浏览器对象模型)重点掌握对象之History对象的属性与方法

History对象是用来把网页浏览历史用类似栈的方式进行表示。 这定义听起来非常的抽象,其实History对象的作用就跟浏览器的前进和后退很像,我们来用几幅图来理解一下。首先我们先回顾一下浏览器的返回上一个页面 和 跳转到下一个页面 这两个功能。 就类似…

JDBC使用

7.2 创建JDBC应用 7.2.1 创建JDBC应用程序的步骤 使用JDBC操作数据库中的数据包括6个基本操作步骤: (1)载入JDBC驱动程序: 首先要在应用程序中加载驱动程序driver,使用Class.forName()方法加载特定的驱动程序&#xf…

【题解单调队列优化dp】划分

划分 分析: 首先,我们目光着眼于部分分 我们尝试用 O ( n 3 ) O(n^3) O(n3)的朴素dp去解决这个问题 f [ i ] [ j ] 表示划分到第 i 个位置,且上一个位置是 j 的最小运行时间是多少 f[i][j]表示划分到第i个位置,且上一个位置是j的…

erlang学习: Mnesia Erlang数据库3

Mnesia数据库删除实现和事务处理 -module(test_mnesia). -include_lib("stdlib/include/qlc.hrl").-record(shop, {item, quantity, cost}). %% API -export([insert/3, select/0, select/1, delete/1, transaction/1,start/0, do_this_once/0]). start() ->mnes…

自然语言处理系列六十九》搜索引擎项目实战》搜索框架技术选型

注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列六十九搜索引擎项目实战》搜索框架技术选型搜索…

(k8s)kubernetes 挂载 minio csi 的方式

一、安装Minio(Minio分布式集群搭建部署_minio集群最少几台-CSDN博客) 生成accessKeyID和secretAccessKey: 二、安装csi-s3插件(在k8s集群上) 首先我们把插件的yaml文件都下载下来,为了保证版本测试的一致性,我们下载…

828华为云征文|基于华为云Flexus云服务器X搭建jumpserver堡垒机软件

文章目录 ❀前言❀jumpserver堡垒机概述❀环境准备❀部署说明❀在线安装❀浏览器访问❀资产添加❀资产授权❀资产登录❀总结 ❀前言 近期华为云推出了最新的华为云Flexus云服务器X,这款云主机在算柔性算力做出了重大变革。华为云Flexus云服务器X基于擎天QingTian架…

QT设置闹钟超时播报

头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTimerEvent> #include<QTime> #include<QTextToSpeech>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic…

一个基于Spring Boot 3、Vue 3 和 Element-Plus 的中后台管理框架,流畅、直观且功能强大

前言 当前市面上的中后台管理系统虽然种类繁多&#xff0c;但在实际使用中仍存在不少痛点&#xff0c;比如技术栈陈旧、性能低下、扩展性差等问题。开发者们常常需要花费大量的时间和精力去处理这些问题&#xff0c;而不是专注于业务逻辑本身。 那么&#xff0c;有没有一个框…

计算赎金信

给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1&#xff1a; 输入&#xff…

使用3DUNet训练自己的数据集(pytorch)— 医疗影像分割

代码:lee-zq/3DUNet-Pytorch: 3DUNet implemented with pytorch (github.com) 文章<cicek16miccai.pdf (uni-freiburg.de)3D U-Net: Learning Dense Volumetric Segmentation

HarmonyOS学习(十)——网络编程

文章目录 1、通过HTTP请求网络2、Web组件2.1、加载本地网页2.2、加载在线网页2.3、网页缩放2.4、文本缩放2.5、web组件事件以及状态说明2.6、处理页面导航 1、通过HTTP请求网络 官方API文档地址&#xff1a;HTTP数据请求-Network Kit数据传输能力-Network Kit&#xff08;网络…

Linux 下 C/C++ 程序编译的过程

目录 一、GCC 工具链二、编译过程1、预处理2、编译3、汇编4、链接 本文将介绍如何将 C/C 语言编写的程序转换成为处理器能够执行的二进制代码的过程&#xff0c;包括四个步骤&#xff1a;预处理&#xff08;Preprocessing&#xff09;编译&#xff08;Compilation&#xff09;汇…