C++系列-多态

🌈个人主页:羽晨同学
💫个人格言:“成为自己未来的主人~”

多态
多态就是不同类型的对象,去做同一个行为,但是产生的结果是不同的。
比如说:
都是动物叫声,猫是喵喵,狗是汪汪,它们的叫声是不相同的。
多态的定义和实现
多态的构成条件
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。
在继承中,构成多态需要两个条件:
- 必须通过基类的指针或者引用调用虚函数。
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。
class Person
{
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}
};
class Student:public Person
{//重写/覆盖virtual void BuyTicket(){cout << "买票-半价" << endl;}
};
class Soldier:public Person
{
public://重写/覆盖virtual void BuyTicket(){cout << "买票-优先" << endl;}
};
//多态条件
//1.虚函数重写
//2.父类指针或者引用调用虚函数
void func(Person& p)
{p.BuyTicket();
}
int main()
{Person p;Student st;Soldier so;func(st);func(so);return 0;
}

你看,这样子就实现了多态。
虚函数
被virtual修饰的函数就叫做虚函数
class Student:public Person
{//重写/覆盖virtual void BuyTicket(){cout << "买票-半价" << endl;}
};
虚函数的重写
虚函数的重写(覆盖):派生类和基类中的某个函数函数名相同,参数相同,返回值相同,就称子类的虚函数重写了基类的虚函数。
class Person
{
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}
};
class Student:public Person
{//重写/覆盖virtual void BuyTicket(){cout << "买票-半价" << endl;}
};
class Soldier:public Person
{
public://重写/覆盖virtual void BuyTicket(){cout << "买票-优先" << endl;}
};
你看,这样其实就是标准的构成了函数重写的代码。
但是,由于继承的存在,所以,其实派生类不写virtual也会构成函数重写。
class Person
{
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}
};
class Student:public Person
{//重写/覆盖virtual void BuyTicket(){cout << "买票-半价" << endl;}
};
class Soldier:public Person
{
public://重写/覆盖void BuyTicket(){cout << "买票-优先" << endl;}
};
比如上面的这样,但是这样子是不规范的,所以我们最好加上virtual。
虚函数重写的两个例外:
协变(基类和派生类的返回值类型不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同,即基类虚函数返回基类对象的指针或者引用,派生类对象返回派生类对象的指针或者引用,这个就叫做协变。
class A{};
class B :public A {};class Person
{
public:virtual A* BuyTicket(){cout << "买票-全价" << endl;return 0;}
};
class Student:public Person
{//重写/覆盖virtual B* BuyTicket(){cout << "买票-半价" << endl;return 0;}
};
class Soldier:public Person
{
public://重写/覆盖virtual B* BuyTicket(){cout << "买票-优先" << endl;return 0;}
};
//多态条件
//1.虚函数重写
//2.父类指针或者引用调用虚函数
void func(Person& p)
{p.BuyTicket();
}
int main()
{Person p;Student st;Soldier so;func(st);func(so);return 0;
}
这样子,我们就实现了协变。
析构函数的重写
我们先来看一下下面的这段代码:
class Person
{
public:virtual ~Person(){cout << "virtual ~Person()" << endl;}
};
class Student:public Person
{
public:protected:int* _ptr = new int[10];
};
int main()
{Person* p1 = new Student;delete p1;return 0;
}
在这段代码当中,我们删除了p1,但是结果调用的是基类的析构函数。

那为什么会这样呢,是因为编译器对析构函数的名字做了特殊的处理,编译后的析构的名字同一处理为了destructor;这样子的话,基类和父类的析构函数就构成了隐藏。
这样子调用的话,那么会造成内存泄漏。
但是,只要我们构成了多态,那么就解决了这个问题。
class Person
{
public:virtual ~Person(){cout << "virtual ~Person()" << endl;}
};
class Student:public Person
{
public:virtual ~Student(){cout << "virtual ~Student()" << endl;}
protected:int* _ptr = new int[10];
};
int main()
{//Student st;Person* p1 = new Student;delete p1;Person* p2 = new Person;delete p2;return 0;
}
override 和final
上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数字母次序写反而无法构成重写,这种错误在编译期间是无法报出的,只有在程序运行时没有到预期结果才会debug,这样子得不偿失。
final:修饰虚函数,表示该虚函数不能再被重写
class A final
{
public:static A CreatObj(){return A();}
private:A(){};
};
class V:public A
{};
int main()
{A::CreatObj();return 0;
}
所以,在这个代码中,会出现报错的问题。
override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class Car {
public:virtual void Drive() {}
};
class Benz :public Car {
public:virtual void Drive() override { cout << "Benz-舒适" << endl; }
};
int main()
{Car a;return 0;
}
重载,覆盖(重写),隐藏(重定义)的对比
重载:
两个函数在同一个作用域。
函数名相同,参数不同。
重写(覆盖)
两个函数分别在基类和派生类的作用域。
函数名,参数,返回值都必须相同(协变除外)
两个函数必须是虚函数
重定义
两个函数分别在基类和派生类的作用域
函数名相同
来年各个基类和派生类的同名函数不构成重写就是重定义
抽象类
在虚函数后面写上=0,则这个函数为纯虚函数,包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象,纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class Car
{
public:virtual void Drive() = 0;
};
class Benz :public Car
{
public:virtual void Drive(){cout << "BWM-舒适" << endl;}
};
class BWM :public Car
{
public:virtual void Drive(){cout << "BWM-操控" << endl;}
};
int main()
{Car a;Benz bz;return 0;
}
在这个代码中,因为基类中有纯虚函数,所以不能实例化出对象,而派生类中对纯虚函数进行了重写,所以可以进行实例化。
多态的原理
虚函数表
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};
int main()
{cout<<sizeof(Base);return 0;
}
大家可以想一想这个结果是什么。
答案是8,那么,这是为什么呢?
我们来仔细看一下Base里面有什么。

我们可以看到的是在这里面还存在了一个虚函数表的指针,这个就是存放虚函数的地址的地方。
class Base
{
public:virtual void func1(){cout << "Base::func1()" << endl;}virtual void func2(){cout << "Base::func2()" << endl;}void func3(){cout << "Base::func3()" << endl;}
private:int _b = 1;
};
class Derive : public Base
{
public:virtual void func1(){cout << "Derive::Func1()" << endl;}
private:int _d = 2;
};
void func1(Base* p)
{p->func1();p->func3();
}
int main()
{Base b;Derive d;func1(&b);func1(&d);return 0;
}

- 通过这个结果,我们可以得到的是,派生类中也有一个虚表的指针,里面存放的有两部分,一个是自己的成员,一个是从基类继承下来的成员。
- 基类b对象和派生类d对象虚表是不一样的,我们这里发现func1完成了重写,所以d中存放的是Derieve::Func1();,所以虚函数的重写也叫覆盖。
- 不是虚函数,函数的指针不会被放进虚表
- 虚表存放在对象里面,虚表里面存放的是虚函数的指针,虚函数存放在代码段那里。
满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象中的找的,不满足多态的函数调用是编译时就确认好的。
动态绑定和静态绑定
在程序编译期间确定了程序的行为,叫做静态绑定。
在程序运行期间,在对象中找的行为,叫做动态绑定
好了,本次的文章就到这里了,我们下次再见。

相关文章:
C++系列-多态
🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 多态 多态就是不同类型的对象,去做同一个行为,但是产生的结果是不同的。 比如说: 都是动物叫声,猫是喵喵,狗是汪汪&am…...
基于C++和Python的进程线程CPU使用率监控工具
文章目录 0. 概述1. 数据可视化示例2. 设计思路2.1 系统架构2.2 设计优势 3. 流程图3.1 C录制程序3.2 Python解析脚本 4. 数据结构说明4.1 CpuUsageData 结构体 5. C录制代码解析5.1 主要模块5.2 关键函数5.2.1 CpuUsageMonitor::Run()5.2.2 CpuUsageMonitor::ComputeCpuUsage(…...
fish-speech语音大模型本地部署
文章目录 fish-speech模型下载编译部署 小结 fish-speech模型 先说下fish-speech模型吧,可以先看下官网。如下: 这就是一个模型,可以根据一个样例声音,构建出自己需要的声音。其实,这个还是有很多用途的;…...
如何写出更牛的验证激励
前言 芯片验证是为了发现芯片中的错误而执行的过程,它是一个破坏性的过程。完备的验证激励可以更有效地发现芯片错误,进而缩短验证周期。合格的验证激励必须能产生所有可能的验证场景(完备性),包括合法和非法的场景,并保持最大的…...
EasyCVR视频汇聚平台:解锁视频监控核心功能,打造高效安全监管体系
随着科技的飞速发展,视频监控技术已成为现代社会安全、企业管理、智慧城市构建等领域不可或缺的一部分。EasyCVR视频汇聚平台作为一款高性能的视频综合管理平台,凭借其强大的视频处理、汇聚与融合能力,在构建智慧安防/视频监控系统中展现出了…...
面对大文件(300G以上)如何加速上传速度
解题思路 采用分片上传,同时每个分片多线程上传可以加速上传速度,上传速度提升10倍左右 在阿里云OSS Go SDK中,bucket.UploadStream 函数并没有直接提供,而是通过 bucket.UploadFile 或者 bucket.PutObject 等函数来实现文件上传…...
基于 Redis 实现消息队列的深入解析
目录 Redis 消息队列简介Redis 消息队列的实现方式 2.1 使用 List 实现简单队列2.2 使用 Pub/Sub 模式实现消息发布与订阅2.3 使用 Stream 实现高级队列 Redis 消息队列的特点与优势Redis 消息队列的应用场景Redis 消息队列的局限性及应对方案总结 Redis 消息队列简介 Redis…...
C++(string类的实现)
1. 迭代器、返回capacity、返回size、判空、c_str、重载[]和clear的实现 string类的迭代器的功能就类似于一个指针,所以我们可以直接使用一个指针来实现迭代器,但如下图可见迭代器有两个,一个是指向的内容可以被修改,另一个则是指…...
nrf 24l01使用方法
1、frequency 频率基础频率2.400G HZ RF_CH RF_CH10 CH2.4G0.01G2.41G 2、逻辑通道6个 pipe 时间片不同,占用同一个频率 发送时,只有一个pipe 接受时可以有6个pipe 3、通讯速率 air data rate rf_dr 寄存器设置 有两种速率 2M 1M RF_DR0 1M ,…...
C语言普及难度三题
先热个身,一个长度为10的整型数组,输出元素的差的max和min。 #include<stdio.h> int main() {int m[10],i0,max,min;for(i0;i<10;i){scanf("%d",&m[i]);}minm[0];maxm[0];for (i 0; i <10; i){if(min>m[i]) min m[i];i…...
10.4每日作业
C1 C2 C1 C2...
日常工作记录:服务器被攻击导致chattr: command not found
在深夜的寂静中,公司的服务器突然遭遇了一场突如其来的攻击。特别是nginx配置文件无法修改,仿佛预示着不祥的预兆,面对这突如其来的灾难,技术人员迅速响应。 这时候需要chattr,但是执行的chattr -i xxx的时候…...
多线程-初阶(1)
本节⽬标 • 认识多线程 • 掌握多线程程序的编写 • 掌握多线程的状态 • 掌握什么是线程不安全及解决思路 • 掌握 synchronized、volatile 关键字 1. 认识线程(Thread) 1.1 概念 1) 线程是什么 ⼀个线程就是⼀个 "执⾏流". 每个线…...
Spring Boot集成encache快速入门Demo
1.什么是encache EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。 Ehcache 特性 优点 快速、简单支持多种缓存策略:LRU、LFU、FIFO 淘汰算法缓存数据有两级:内存和磁盘&a…...
【C语言】数组练习
【C语言】数组练习 练习1:多个字符从两端移动,向中间汇聚练习2、二分查找 练习1:多个字符从两端移动,向中间汇聚 编写代码,演示多个字符从两端移动,向中间汇聚 练习2、二分查找 在⼀个升序的数组中查找指…...
微服务实战——ElasticSearch(保存)
商品上架——ElasticSearch(保存) 0.商城架构图 1.商品Mapping 分析:商品上架在 es 中是存 sku 还是 spu ? 检索的时候输入名字,是需要按照 sku 的 title 进行全文检索的检索使用商品规格,规格是 spu 的…...
leetcode练习 路径总和II
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1: 输入:root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22 输出&a…...
使用Three.js库创建的简单WebGL应用程序,主要用于展示具有不同透明度和缩放比例的圆环列
上述HTML文档是一个使用Three.js库创建的简单WebGL应用程序,主要用于展示具有不同透明度和缩放比例的圆环列。以下是代码的详细解释: HTML结构: 文档类型声明为HTML5。<html>标签设置了语言属性为英语(lang"en")…...
Redis: 集群架构,优缺点和数据分区方式和算法
集群 集群指的就是一组计算机作为一个整体向用户提供一组网络资源 我就举一个简单的例子,比如百度,在北京和你在上海访问的百度是同一个服务器吗?答案肯定是不是的,每一个应用可以部署在不同的地方,但是我们提供的服务…...
负载均衡可以在网络模型的哪一层?
一、网络模型概述 网络模型是用于描述网络通信过程和网络服务的抽象框架。最常见的网络模型有两种:OSI(开放式系统互联)模型和TCP/IP模型。 OSI模型 OSI(Open Systems Interconnection)模型是由国际标准化组织&…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
