当前位置: 首页 > news >正文

【C++进阶02】多态

在这里插入图片描述

一、多态的概念及定义

1.1 多态的概念

多态简单来说就是多种形态
同一个行为,不同对象去完成时
会产生出不同的状态
多态分为静态多态动态多态
静态多态指的是编译时
在程序编译期间确定了程序的行为
比如:函数重载
动态多态指的是运行时
在程序运行期间,根据具体拿到的类型
确定程序的具体行为,调用具体的函数

1.2 在继承中要构成多态的两个条件

  1. 必须通过父类指针或引用调用虚函数

  2. 虚函数的重写
    函数名、参数类型、返回值都要相同

被virtual修饰的类成员函数称为虚函数

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

1.3 虚函数的重写(覆盖)

派生类中有一个跟基类完全相同的虚函数
(即派生类虚函数与基类虚函数的返回值类
型、函数名字、参数列表完全相同)
称子类的虚函数重写了基类的虚函数

普通函数的继承是实现继承
派生类继承了基类函数,可以使用函数
继承的是函数的实现
虚函数的继承是接口继承
派生类继承的是基类虚函数的接口
目的是为了重写,达成多态,继承的是接口
如果不实现多态,不要把函数定义成虚函数

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }// 只要父类析构加了virtual就构成多态,子类加不加都可以正确释放virtual ~Person() { cout << "~Person" << endl; };
};class Student : public Person {
public: // 子类可以不写virtual,因为他继承父类的接口,重写实现virtual void BuyTicket() { cout << "买票-半价" << endl; }~Student() { cout << "~Student" << endl; }
};void Func(Person& p)
{ p.BuyTicket(); }int main()
{
Person ps;
Student st;// 构成多态后
Func(ps); // 传父类调用父类的虚函数
Func(st); // 传子类调用子类的虚函数return 0;
}

1.4 协变

如果是父子关系的指针或引用
返回值可以不同也构成多态

class A{};
class B : public A {};
class Person {
public:virtual A* f() {return new A;}
};
class Student : public Person {
public:virtual B* f() {return new B;}
};

1.5 final和override

final: 修饰虚函数
表示该虚函数不能再被重写
现实中不常用,不能实现多态的虚函数
意义不大

class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() {cout << "Benz-舒适" << endl;}
};

override: 检查派生类虚函数
是否重写了基类某个虚函数
如果没有重写编译报错

class Car{
public:virtual void Drive(){}
};
class Benz :public Car {
public:virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

1.6 重载、覆盖(重写)、隐藏(重定义)的对比

面试题经常被问到
在这里插入图片描述

1.7 抽象类

在虚函数后面加上 =0
这个函数就叫纯虚函数
包含纯虚函数的类叫做抽象类
抽象类不能实例化出对象
派生类继承后也不能实例化出对象
只有重写纯虚函数,派生类才能实例化出对象

class Car
{
public:// 纯虚函数 --- 抽象类virtual void Drive() = 0;
};int main()
{Car car; // 无法实例化对象return 0;
}

二、多态的原理

2.1 虚函数表

这里常考一道笔试题:sizeof(Base)是多少?

class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};int main()
{cout << sizeof(Base) << endl;return 0;
}

在32位操作系统下是8 bit
在64位操作系统下是16 bit
通过调试发现还有个指针_vfptr
这个指针叫做虚函数表指针
本质是指针数组
用来存放虚函数的地址
在这里插入图片描述

对象中存的是虚表指针
虚表存的是虚函数指针
虚函数和普通函数一样的
都是存在代码段的

2.2 多态的原理

通过下面代码观察父子类
的虚表之间的关系

class Base
{
public:virtual void Func(){cout << "Base::Func1()" << endl;}
};
class Derive : public Base
{
public:virtual void Func(){cout << "Derive::Func1()" << endl;}
};void Test(Base* p)
{p->Func();
}int main()
{Base b;Derive d;Test(&b);return 0;
}

监视窗口
在这里插入图片描述
通过监视窗口可以发现
派生类对象d中也有一个虚表指针
通过地址发现基类和派生类的虚表是不一样的

虚函数表本质是存虚函数指针的指针数组
一般情况这个数组最后面放了一个nullptr

结论:
观察下图红色箭头
当传过来的是父类对象的地址
p->Func在父类的虚表中找对应的虚函数Func地址
当传过来的是子类对象的地址
p->Func在子类的虚表中找对应的虚函数Func地址

这样就实现不同对象的同一行为
展现的不同状态
在这里插入图片描述
当父类有虚函数而子类没有虚函数
也没有重名函数
子类是不能继承父类的虚表指针
子类会生成一个虚表指针
存父类的虚函数地址
当子类有虚函数而父类没有
父类也就不会有虚表
因为子类有的东西父类不一定有

派生类的虚表生成:
a.先将基类中的虚表内容拷贝一份到派生类虚表中
b.如果派生类重写了基类中某个虚函数
用派生类自己的虚函数覆盖虚表中基类的虚函数
c.派生类自己新增加的虚函数按其在派生类中的
声明次序增加到派生类虚表的最后

2.3 在多态下建议把基类的析构函数定义成虚函数

如果基类析构函数不是虚函数
就调不到派生类的析构函数
(指针类型是父类,所以调用父类的析构函数)
从而造成内存泄漏

class Person {
public:virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:virtual ~Student() { cout << "~Student()" << endl; }
};int main()
{Person* ps = new Student;delete ps;return 0;
}

形成多态的条件之一便是
只能通过父类去调用
所以子类对象只能强转成父类类型
如果父类的析构函数不是虚函数
那子类便调不到自己的析构函数
因为子类对象的类型是父类
所以只能调用父类的析构函数
子类成员无法释放从而造成内存泄漏
父类析构函数定义成虚函数便能解决问题

✨✨✨✨✨✨✨✨
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见
✨✨✨✨✨✨✨✨

相关文章:

【C++进阶02】多态

一、多态的概念及定义 1.1 多态的概念 多态简单来说就是多种形态 同一个行为&#xff0c;不同对象去完成时 会产生出不同的状态 多态分为静态多态和动态多态 静态多态指的是编译时 在程序编译期间确定了程序的行为 比如&#xff1a;函数重载 动态多态指的是运行时 在程序运行…...

PHP开发日志——循环和条件语句嵌套不同,效率不同(循环内加入条件语句,条件语句判断后加入循环,array_map函数中加入条件语句)

十多年前开发框架时&#xff0c;为了效率不断试过各种代码写法&#xff0c;今天又遇到了&#xff0c;想想php8时代会不会有所变化&#xff0c;结果其实也还是和当年一样&#xff0c;但当年没写博客&#xff0c;但现在可以把数据记录下来了。 PHP_loop_ireflies_dark_forest 项目…...

【Seata源码学习 】 扫描@GlobalTransaction注解 篇一

1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制&#xff0c;在应用上下文启动时&#xff0c;会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…...

DBA-MySql面试问题及答案-上

文章目录 1.什么是数据库?2.如何查看某个操作的语法?3.MySql的存储引擎有哪些?4.常用的2种存储引擎&#xff1f;6.可以针对表设置引擎吗&#xff1f;如何设置&#xff1f;6.选择合适的存储引擎&#xff1f;7.选择合适的数据类型8.char & varchar9.Mysql字符集10.如何选择…...

网络爬虫之Ajax动态数据采集

动态数据采集 规则 有时候我们在用 requests 抓取页面的时候&#xff0c;得到的结果可能和在浏览器中看到的不一样&#xff0c;在浏览器中可以看到正常显示的页面教据&#xff0c;但是使用 requests 得到的结果并没有&#xff0c;这是因为requests 获取的都是原始的 HTML 文档…...

c语言的初始学习(练习)

##初学c语言---MOOC浙江大学翁恺先生学习c语言 那么我们先看看这个题目吧&#xff0c;这是初始语法的应用。 记住&#xff0c;我们的程序是按步骤执行的&#xff0c;并不是在不同的两行同时进行。 程序设计&#xff1a;1.了解题目的需要&#xff0c;几个变量需要用到&#x…...

研究论文 2022-Oncoimmunology:AI+癌RNA-seq数据 识别细胞景观

Wang, Xin, et al. "Deep learning using bulk RNA-seq data expands cell landscape identification in tumor microenvironment." Oncoimmunology 11.1 (2022): 2043662. https://www.tandfonline.com/doi/full/10.1080/2162402X.2022.2043662 被引次数&#xff1…...

ChatGPT4与ArcGIS Pro3助力AI 地理空间分析和可视化及助力科研论文写作

在地学领域&#xff0c;ArcGIS几乎成为了每位科研工作者作图、数据分析的必备工具&#xff0c;而ArcGIS Pro3除了良好地继承了ArcMap强大的数据管理、制图、空间分析等能力&#xff0c;还具有二三维融合、大数据、矢量切片制作及发布、任务工作流、时空立方体等特色功能&#x…...

okhttp系列-一些上限值

1.正在执行的任务数量最大值是64 异步请求放入readyAsyncCalls后&#xff0c;遍历readyAsyncCalls取出任务去执行的时候&#xff0c;如果发现runningAsyncCalls的数量大于等于64&#xff0c;就不从readyAsyncCalls取出任务执行。 public final class Dispatcher {private int …...

C++面向对象(OOP)编程-STL详解(vector)

本文主要介绍STL六大组件&#xff0c;并主要介绍一些容器的使用。 目录 1 泛型编程 2 CSTL 3 STL 六大组件 4 容器 4.1 顺序性容器 4.1.1 顺序性容器的使用场景 4.2 关联式容器 4.2.1 关联式容器的使用场景 4.3 容器适配器 4.3.1 容器适配器的使用场景 5 具体容器的…...

postman几种常见的请求方式

1、get请求直接拼URL形式 对于http接口&#xff0c;有get和post两种请求方式&#xff0c;当接口说明中未明确post中入参必须是json串时&#xff0c;均可用url方式请求 参数既可以写到URL中&#xff0c;也可写到参数列表中&#xff0c;都一样&#xff0c;请求时候都是拼URL 2&am…...

openai最新探索:超级对齐是否可行?

前言 今天来介绍一篇openai最新的paper&#xff1a;弱到强的对齐。 openai专门成立了一个团队来做大模型的超级对齐即superhuman model&#xff0c;之前chatgpt取得成功依赖RLHF即依赖人类反馈&#xff0c;但是作者期望的superhuman model将会是一个能够处理各种复杂问题的强…...

本地websocket服务端结合cpolar内网穿透实现公网访问

文章目录 1. Java 服务端demo环境2. 在pom文件引入第三包封装的netty框架maven坐标3. 创建服务端,以接口模式调用,方便外部调用4. 启动服务,出现以下信息表示启动成功,暴露端口默认99995. 创建隧道映射内网端口6. 查看状态->在线隧道,复制所创建隧道的公网地址加端口号7. 以…...

关于“Python”的核心知识点整理大全37

目录 13.6.2 响应外星人和飞船碰撞 game_stats.py settings.py alien_invasion.py game_functions.py ship.py 注意 13.6.3 有外星人到达屏幕底端 game_functions.py 13.6.4 游戏结束 game_stats.py game_functions.py 13.7 确定应运行游戏的哪些部分 alien_inva…...

Vivado中的FFT IP核使用(含代码)

本文介绍了Vidado中FFT IP核的使用&#xff0c;具体内容为&#xff1a;调用IP核>>配置界面介绍>>IP核端口介绍>>MATLAB生成测试数据>>测试verilogHDL>>TestBench仿真>>结果验证>>FFT运算。 1、调用IP核 该IP核对应手册pg109_xfft.pd…...

​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化

2022年亚马逊云科技re:Invent盛会于近日在拉斯维加斯成功召开&#xff0c;吸引了众多业界精英和创新者。亚马逊云科技边缘服务副总裁Jan Hofmeyr在演讲中分享了关于亚马逊云科技海外服务器边缘计算的最新发展和创新成果&#xff0c;引发与会者热烈关注。 re:Invent的核心主题是…...

什么是“人机协同”机器学习?

“人机协同”&#xff08;HITL&#xff09;是人工智能的一个分支&#xff0c;它同时利用人类智能和机器智能来创建机器学习模型。在传统的“人机协同”方法中&#xff0c;人们会参与一个良性循环&#xff0c;在其中训练、调整和测试特定算法。通常&#xff0c;它的工作方式如下…...

数学建模笔记-拟合算法

内容&#xff1a;拟合算法 一.概念&#xff1a; 拟合的结果就是找到一个确定的曲线 二.最小二乘法&#xff1a; 1. 2.最小二乘法的二表示的是平方的那个2 3.求解最小二乘法&#xff1a; 三.评价拟合的好坏 1.总体评分和SST&#xff1a; 2.误差平方和SSE&#xff1a; 3.回…...

非线性约束的优化问题_序列二次规划算法代码

1. 理论部分 2. 序列二次规划算法代码及解析 3.完整代码 1.理论部分 a.约束优化问题的极值条件 库恩塔克条件(Kuhn-Tucker conditions&#xff0c;KT条件)是确定某点为极值点的必要条件。如果所讨论的规划是凸规划&#xff0c;那么库恩-塔克条件也是充分条件。 &#xff…...

【数据结构之顺序表】

数据结构学习笔记---002 数据结构之顺序表1、介绍线性表1.1、什么是线性表? 2、什么是顺序表?2.1、概念及结构2.2、顺序表的分类 3、顺序表接口的实现3.1、顺序表动态存储结构的Seqlist.h3.1.1、定义顺序表的动态存储结构3.1.2、声明顺序表各个接口的函数 3.2、顺序表动态存储…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...