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)模型是由国际标准化组织&…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...

Element-Plus:popconfirm与tooltip一起使用不生效?
你们好,我是金金金。 场景 我正在使用Element-plus组件库当中的el-popconfirm和el-tooltip,产品要求是两个需要结合一起使用,也就是鼠标悬浮上去有提示文字,并且点击之后需要出现气泡确认框 代码 <el-popconfirm title"是…...

C# WPF 左右布局实现学习笔记(1)
开发流程视频: https://www.youtube.com/watch?vCkHyDYeImjY&ab_channelC%23DesignPro Git源码: GitHub - CSharpDesignPro/Page-Navigation-using-MVVM: WPF - Page Navigation using MVVM 1. 新建工程 新建WPF应用(.NET Framework) 2.…...
智能体革命:企业如何构建自主决策的AI代理?
OpenAI智能代理构建实用指南详解 随着大型语言模型(LLM)在推理、多模态理解和工具调用能力上的进步,智能代理(Agents)成为自动化领域的新突破。与传统软件仅帮助用户自动化流程不同,智能代理能够自主执行工…...

Xcode 16.2 版本 pod init 报错
Xcode 版本升级到 16.2 后,项目执行 pod init 报错; ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchron…...