多态的使用和原理(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…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10pip3.10) 一:前言二:安装编译依赖二:安装Python3.10三:安装PIP3.10四:安装Paddlepaddle基础框架4.1…...
LangChain【6】之输出解析器:结构化LLM响应的关键工具
文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器?1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...
2.2.2 ASPICE的需求分析
ASPICE的需求分析是汽车软件开发过程中至关重要的一环,它涉及到对需求进行详细分析、验证和确认,以确保软件产品能够满足客户和用户的需求。在ASPICE中,需求分析的关键步骤包括: 需求细化:将从需求收集阶段获得的高层需…...
【Ftrace 专栏】Ftrace 参考博文
ftrace、perf、bcc、bpftrace、ply、simple_perf的使用Ftrace 基本用法Linux 利用 ftrace 分析内核调用如何利用ftrace精确跟踪特定进程调度信息使用 ftrace 进行追踪延迟Linux-培训笔记-ftracehttps://www.kernel.org/doc/html/v4.18/trace/events.htmlhttps://blog.csdn.net/…...
Python的__call__ 方法
在 Python 中,__call__ 是一个特殊的魔术方法(magic method),它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时(例如 obj()),Python 会自动调用该对象的 __call__ 方法…...
python读取SQLite表个并生成pdf文件
代码用于创建含50列的SQLite数据库并插入500行随机浮点数据,随后读取数据,通过ReportLab生成横向PDF表格,包含格式化(两位小数)及表头、网格线等美观样式。 # 导入所需库 import sqlite3 # 用于操作…...