多态的使用和原理(c++详解)
一、多态的概念
多态顾名思义就是多种形态,它分为编译时的多态(静态多态)和运行时的多态(动态多态),编译时多态(静态多态)就是函数重载,模板等,通过不同的参数来完成对不同的函数的调用(即生成多种形态)并且这个过程在编译阶段就已经完成。
动态多态是在运行时根据对象的实际类型来确定调用函数的哪个版本,完成不同的⾏为。
二、多态构成条件
1.虚函数
在类成员函数的返回类型前面添加virtual关键字即为虚函数,注意:虚函数只能定义于普通成员函数,构造函数以及类外函数不能定义虚函数。
2.虚函数的重写
虚函数重写指的是子类(派生类)对父类(基类)的重写。重写的要求是子类虚函数的返回值,函数名,参数类型必须和父类一模一样。但函数的实现逻辑不用相同。
这里如果虚函数的重写没有加virtual,但是父类加了virtual那么子类依旧保持virtual的性质,也可构成重写。
注意:对虚函数重写并没有要求缺省参数要相同,但在这里强烈建议把缺省参数设为相同值,要不然会给你带来很大的弊端和误导性。接下来我会讲到。
3.调用方式
要实现多态效果,第⼀必须是基类的指针或引⽤,因为只有基类的指针或引⽤才能既指向派⽣类对象又能指向基类;第⼆派⽣类必须对基类的虚函数重写/覆盖,重写或者覆盖了,派⽣类才能有不同的函数,多态的不同形态效果才能达到。
⽐如火车买票这个操作,当普通⼈买票时,是全价买票;学⽣买票时,是优惠买票;军⼈买票时是优先买票,我们就可以用多态来实现,如下:
#include<iostream>
using namespace std;
class ticket
{
public:virtual void func(){cout << "普通票" << endl;}
private:
};
class student:public ticket
{
public:virtual void func(){cout << "学生票" << endl;}
private:
};
void fm(ticket& pu)
{pu.func();
}
int main()
{ticket tk;student stu;fm(tk);fm(stu);return 0;
}
4.override和final的修饰
override关键字:因为多态的实现细节要求太多了特别是对虚函数的重写,因此C++11提供了override,可以帮助⽤⼾来检查虚函数的重写是否正确,需要放在重写的函数参数列表后面。
final关键字:如果不想子类对该虚函数进行重写的话就可以使用final关键字,放在函数名后面。
5.协变
刚才我们说了虚函数的重写一定要满足子类虚函数的返回值,函数名,参数类型必须和父类相同。协变是个例外情况。当子类重写父类虚函数时,若与父类虚函数返回值类型不同,即父类虚函数返回父类对象的指针或引用,子类虚函数返回子类对象的指针或引用,此时称为协变。协变的实际意义并不⼤,所以我们了解⼀下即可。
代码示例:
class A{};
class B :public A{};
class ticket
{
public:virtual ticket* func()//ticket也可以是A{cout << "普通票" << endl;return this;}
private:
};
class student:public ticket
{
public:virtual student* func() override//student也可以是B{cout << "学生票" << endl;return this;}
private:
};
析构函数的重写
父类的析构函数为虚函数,此时派⽣类析构函数只要定义,⽆论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派⽣类析构函数名字不同看起来不符合重写的规则,实际上编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统⼀处理成destructor,所以基类的析构函数加了vialtual修饰,派⽣类的析构函数就构成重写。
A* p1 = new A;
A* p2 = new B;
delete p1;
delete p2;
假设B是A的子类上⾯的代码如果~A(),不加virtual,那么delete p2时只调⽤A的析构函数,没有调⽤B的析构函数,就会导致内存泄漏问题。
三、纯虚函数和抽象类
在虚函数的参数列表后⾯写上=0,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义因为要被子类重写,但是语法上可以实现),只要声明即可。包含纯虚函数的类叫做抽象类,抽象类不能实例化出对象,如果子类继承后不重写纯虚函数,那么子类也是抽象类。纯虚函数某种程度上强制了子类重写虚函数,因为不重写实例化不出对象。
四、多态原理
在分析对象的储存空间时,我们讲过对于同一个类实例化出的不同对象,这些对象使用的函数都是相同的,不同的是它们成员变量。所以每个对象只需要对成员变量进行储存,不用对成员函数进行储存,每一个对象使用的都是这个类的公共成员函数。
c++为虚函数单独设立了一块区域来储存虚函数的地址,叫做虚表,而这块区域其实 就是一个函数指针数组。即用来储存函数指针的一个数组。那么父类和子类就各自有一个虚表,在对象实例化的时候就会隐含(隐含:类似于成员函数里面看不见的this指针一样)着一个指针——虚表指针,来指向虚表。
#include<iostream>
using namespace std;
class A
{
public:virtual void func(){}
};
class B
{
public:void func(){}
};
int main()
{A a;B b;cout << "a:" << sizeof(a) << endl;cout << "b:" << sizeof(b) << endl;return 0;
}
而虚表指针也是需要占用空间的大家可以自行地去运行一下以上代码,输出结果为:
a:4(或8,即32位与64位机器的区别)
b:1
所以在调用对象的虚函数时就跟以什么类型的形式调用无关,而是跟这个对象实例化时具体类型有关。
#include<iostream>
using namespace std;
class ticket
{
public:virtual void func(){cout << "普通票" << endl;}
};
class student :public ticket
{
public:virtual void func() {cout << "学生票" << endl;}
};
int main()
{ticket* tk = new student;tk->func();return 0;
}
以上的输出结果是“学生票”。
注意:根据切片原理,子类可强制类型转化为父类,父类不能强制类型转化为子类。
五、练习
以下程序输出结果是什么()
- A:A->0
- B:B->1
- C:A->1
- D:B->0
- E:编译出错
- F:以上都不正确
这里虽然B类的func成员没有写virtual关键字,但它是由A继承下来的依旧保留virtual的性质,然后因为重写并为要求参数的缺省值相同,所以这里构成函数的重写。再来看主函数main,p调用了test,而test是A的成员函数隐含了一个const A* (this指针)的参数类型,p传到test函数满足多态,所以这里调用的是B的func。但是这里有个坑,该题的输出结果并不是“B->0”,而是“B->1”。
要注意重写只是重写了函数的实现,也就是说实现多态的时候相当于调用的是父类的接口声明和子类的函数实现,而并不关心子类的函数接口声明。
所以在我们自己写虚函数的时候,最好把缺省参数设为相同值,要不然会给你带来很大的误导性。
相关文章:

多态的使用和原理(c++详解)
一、多态的概念 多态顾名思义就是多种形态,它分为编译时的多态(静态多态)和运行时的多态(动态多态),编译时多态(静态多态)就是函数重载,模板等,通过不同的参数…...

OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【Trace调测】
往期知识点记录: 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~ 持续更新中…… 基本概念 Trace调测旨在帮助开发者获取内核的运行流程,…...
Lombok 在 IntelliJ IDEA 中的使用步骤
Lombok 是一个非常流行的 Java 库,它通过注解简化 Java 类的开发,特别是在处理 POJO(Plain Old Java Objects)类时,如生成 getter、setter、toString 等常用方法。Lombok 在减少样板代码(boilerplate code&…...

计算机网络 --- Socket 编程
序言 在上一篇文章中,我们介绍了 协议,协议就是一种约定,规范了双方通信需要遵循的规则、格式和流程,以确保信息能够被准确地传递、接收和理解。 在这篇文章中我们将介绍怎么进行跨网络数据传输,在这一过程中相信大家…...

git笔记之在多个分支中复用某个分支提交的更改
git笔记之在多个分支中复用某个分支提交的更改 code review! 文章目录 git笔记之在多个分支中复用某个分支提交的更改1.实现该功能的 Bash 脚本示例2.这个脚本是否可以处理新添加的文件?3.该脚本使用前,应先使用下述脚本重置本地仓库所有分支与远程保持一…...

HTML、CSS
初识web前端 web标准 Web标准也称为网页标准,由一系列的标准组成,大部分由W3C (World Wide Web Consortium,万维网联盟) 负责制定。三个组成部分: HTML: 负责网页的结构(页面元素和内容)。CSS: 负责网页的表现(页面元素的外观、位置等页面样…...
数据文件(0)
一、使用场景 1、字典数据 对于一些数据量不大的配置类数据,放到数据库中占用数据库资源,可以放到代码中维护。比如 (1)字段少业务单一:做成枚举; (2)字段多业务复杂:…...
Go语言并发模式详解:深入理解管道与上下文的高级用法
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在Go语言中,并发编程是其最强大的特性之一。合理地使用并发模式,可以让我们的程序高效而优雅地处理复杂的任务。在本文中,我们将深入探讨Go语言中的一些高级并发模式,包括管道的技巧和上下文包的应用。通过丰…...

标准文档流解析及 CSS 中的相关特性
目录 非 VIP 用户可前往公众号回复“css”进行免费阅读 标准文档流特点 空白折叠现象 高矮不齐、底边对齐 自动换行,一行写不满,换行写 标准文档流中的元素等级 HTML 与 CSS 中的标签分类总结 块级元素和行内元素的相互转换 块级转行内 行内转块级 display 非 VIP…...
水下攻防面试题
水下攻防面试题通常涉及对水下环境的理解、水下安全操作、水下技术应用以及攻防策略等多个方面。由于具体的面试题可能因组织、职位和目的的不同而有所差异,以下是一些可能出现在水下攻防面试中的典型问题及其参考答案框架: 一、基础概念与理解 什么是水下攻防? 水下攻防是…...

vmware 虚拟机多屏幕或添加屏幕
vmware 虚拟机多屏幕或添加屏幕 前置条件 vmware 安装 vmware tools 虚拟机系统支持多屏幕 物理上有至少两个屏幕,就是物理机上接至少一个屏幕 方法 虚拟机上点设置,需要在虚拟机关机时进行 ctrl alt enter 让当前虚拟机全屏 鼠标移动到屏幕虚拟机…...
鹏哥C语言49-51---第6次作业:循环语句 for 和 while
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> //-----------------------------------------------------------------------------------------------第六次作业:for循环等 //--------------------------------------------------------------------…...

springboot中药材进存销管理系统
基于springbootvue实现的中药材进存销管理系统 (源码L文ppt)4-079 4 系统总体设计 4.1系统功能结构设计图 根据需求说明设计系统各功能模块。采用模块化设计方法实现一个复杂结构进行简化,分成一个个小的容易解决的板块,然…...

GitHub上图像超分开源项目推荐【持续更新】
SRCNN 介绍:SRCNN(Super-Resolution Convolutional Neural Network)是一种用于图像超分辨率的卷积神经网络。它由Dong等人在2014年提出,是早期的深度学习方法之一,用于提高图像的分辨率。SRCNN通过学习低分辨率&#…...

浅谈软件测试的基础知识(1)
文章目录 一、什么是测试1.1、生活中的测试案例1.2、为什么需要进行软件测试 二、测试和开发的区别2.1、调试和测试的区别 四、测试人员需具备哪些素质五、软件的生命周期六、软件测试的生命周期七、设计测试用例的方法[!]7.1、什么是测试用例7.2、测试用例作用 八、走测试岗位…...

Mac 上哪个剪切板增强工具比较好用? 好用剪切板工具推荐
在日常文字编辑中,我们经常需要重复使用复制的内容。然而,新内容一旦复制,旧内容就会被覆盖。因此,选择一款易用高效的剪贴板工具成为了许多人的需求。本文整理了一些适用于 macOS 系统的优秀剪贴板增强工具,欢迎大家下…...

基于opencv的车牌检测和识别系统(代码+教程)
车牌检测与识别技术详解 车牌检测和识别(License Plate Recognition, LPR)是一项重要的计算机视觉任务,它在交通管理、安全监控以及智能门禁系统等多个领域都有着广泛的应用。随着深度学习技术的发展,LPR系统的准确性和鲁棒性得到…...

list(二) (list模拟实现)
首先进行大框架 先写基本的结点类 有data next prev template<class T>class ListNode//或者使用struct 就不用在写public声明公有{public://这里不仅仅是成员函数 成员变量也要公有化 ListNode<T>* _next;ListNode<T>* _prev;T _data;}之后是链表list类…...

[Linux]从零开始的泰山派系统安装与远程教程
一、前言 泰山派买回来也有一阵子了,最近慢慢开始研究。当然,学习这种Linux的开发板的第一步就是安装系统,对于RK系列的芯片系统安装有专门的软件,所有在系统安装方面比较简单。更多的还是我们应该怎么去编译系统,这一…...
Python国产新 ORM 框架 fastzdp_sqlmodel 快速入门教程
创建模型 from typing import Optional from sqlmodel import Field, SQLModel import fastzdp_sqlmodel as fasmclass Hero(SQLModel, tableTrue):id: Optional[int] Field(defaultNone, primary_keyTrue)name: strsecret_name: strage: Optional[int] None创建表 from ty…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”
案例: 某医药分销企业,主要经营各类药品的批发与零售。由于药品的特殊性,效期管理至关重要,但该企业一直面临效期问题的困扰。在未使用WMS系统之前,其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...