C++入门小馆 :多态

嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let's go!

我的博客:yuanManGan

我的专栏:C++入门小馆 C言雅韵集 数据结构漫游记  闲言碎语小记坊 题山采玉 领略算法真谛

目录

多态的概念:

构成多态的条件:

虚函数:

虚函数的重写/覆盖:

• 协变(了解)

析构函数的重写:

override 和 final关键字

重载/重写/隐藏的对⽐

 纯虚函数和抽象类

多态的原理:

虚函数表指针:

虚函数表:


面向对象三大特性:封装,继承,多态。今天来讲讲最后一个多态。

多态的概念:

多态是针对函数来说的,当函数名相同时,我们传入不同的参数会出现不同的效果,这就是多态。多态又分为静态多态,和动态多态,静态指的一般是函数重载和模板(又叫作编译时多态),而我们今天的重点是动态多态(也叫作运行时多态)

动态多态就是,当你传入不同的对象时(函数),会出现不一样的结果,比如我们买火车票,如果你是学生会打折,成人就是全票,而军人就是优先买票。再⽐如,同样是动物叫的⼀个⾏为(函数),传猫对象过去,就是”(>^ω^<) 喵“,传狗对象过去,就是"汪汪"。

构成多态的条件:

必须是基类的指针或者引⽤调⽤虚函数
被调⽤的函数必须是虚函数,并且完成了虚函数重写/覆盖。
多态是⼀个继承关系的下的类对象,去调⽤同⼀函数,产⽣了不同的⾏为。⽐如Student继承了
Person。Person对象买票全价,Student对象优惠买票。
说明:要实现多态效果,第⼀必须是 基类的指针或引⽤ ,因为只有基类的指针或引⽤才能既指向基类 对象⼜指向派⽣类对象;第⼆派⽣类必须对基类的虚函数 完成重写/覆盖 ,重写或者覆盖了,基类和派 ⽣类之间才能有不同的函数,多态的不同形态效果才能达到。

这里补充一下:

虚函数:

类成员函数前⾯加virtual修饰,那么这个成员函数被称为虚函数。注意⾮成员函数不能加virtual修
饰。

虚函数的重写/覆盖:

派生类里面的一个虚函数和父类的虚函数有三同:
1.返回值相同
2.参数相同
3.函数名相同
注意:我们如果父类加了virtual,子类可以不加virtual依然可以构成虚函数,但实践中不建议这样做,选择题经常有这个坑。
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
};
void Func(Person* ptr)
{// 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket// 但是跟ptr没关系,⽽是由ptr指向的对象决定的。ptr->BuyTicket();
}
int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}

 

class Animal
{
public:virtual void talk() const{}
};
class Dog : public Animal
{
public:virtual void talk() const{std::cout << "汪汪" << std::endl;}
};
class Cat : public Animal
{
public:virtual void talk() const{std::cout << "(>^ω^<)喵" << std::endl;}
};
void letsHear(const Animal& animal)
{animal.talk();
}
int main()
{Cat cat;Dog dog;letsHear(cat);letsHear(dog);return 0;
}

这里的对虚函数的重写,其实是重写的实现。

协变(了解)

派⽣类重写基类虚函数时,与基类虚函数返回值类型可以不同,可以都是 基类对象的指针或者引
⽤,而这种情况我们称之为协变。
class A {};
class B : public A {};
class Person {
public:virtual A* BuyTicket(){cout << "买票-全价" << endl;return nullptr;}
};
class Student : public Person {
public:virtual B* BuyTicket(){cout << "买票-打折" << endl;return nullptr;}
};
void Func(Person* ptr)
{ptr->BuyTicket();
}
int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}

这样写没有很大的意义,我们很少这么实现。

析构函数的重写:

我们定义了两个A类型的指针分别指向A和B对象,但我们析构时为什么没有析构B对象呢?因为我们的p2是指向A类型的,然后我们析构是调用A类型的析构。那我们该怎样调用B的析构函数呢?这里就要用到多态了,但我们的析构的函数名不是不相同吗,怎么实现重写呢?其实编译器会将你写的析构函数的函数名看成一样的Destruct,这就导致我们继承遇到的析构函数为什么会隐藏,这里将A的析构写成隐藏就可以解决了。

override 和 final关键字

final关键字我们之前用来定义一个不能被继承的类,这里可以用它来定义一个不能被重写的虚函数。

override 用来检查一个虚函数是否被重写,没有就会报错。

重载/重写/隐藏的对⽐

 纯虚函数和抽象类

在虚函数的后⾯写上 =0 ,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义因为要被 派⽣类重写,但是语法上可以实现),只要声明即可。包含纯虚函数的类叫做抽象类,抽象类不能实例 化出对象,如果派⽣类继承后不重写纯虚函数,那么派⽣类也是抽象类。纯虚函数某种程度上强制了 派⽣类重写虚函数,因为不重写实例化不出对象。

多态的原理:

虚函数表指针:

该程序的运行结果(32位的环境下)是什么呢?

有人说是8不就是两个成员吗,内存对齐一下就是8,其实我们有虚函数的类里面还有一个虚函数表指针,指向虚函数表。

 所以答案是12。

我们实现虚函数的重写其实就是将虚函数表指针进行重写。使他指向不同的对象,我们无论传入的派生类是怎么样的,传入Func函数都会进行截断,是它只出现父类,然后通过虚函数表指针调用函数,这就形成了多态。

虚函数表:

基类对象的虚函数表中存放基类所有虚函数的地址。同类型的对象共⽤同⼀张虚表,不同类型的对
象各⾃有独⽴的虚表,所以基类和派⽣类有各⾃独⽴的虚表。
所以我们不要什么函数都弄成虚函数。
派⽣类由两部分构成,继承下来的基类和⾃⼰的成员,⼀般情况下,继承下来的基类中有虚函数表
指针,⾃⼰就不会再⽣成虚函数表指针。但是要注意的这⾥继承下来的基类部分虚函数表指针和基
类对象的虚函数表指针不是同⼀个,就像基类对象的成员和派⽣类对象中的基类对象成员也独⽴
的。
派⽣类中重写的基类的虚函数,派⽣类的虚函数表中对应的虚函数就会被覆盖成派⽣类重写的虚函
数地址。
虚函数表本质是⼀个存虚函数指针的指针数组,⼀般情况这个数组最后⾯放了⼀个0x00000000标
记。(这个C++并没有进⾏规定,各个编译器⾃⾏定义的,vs系列编译器会再后⾯放个0x00000000
标记,g++系列编译不会放)
虚函数存在哪的?虚函数和普通函数⼀样的,编译好后是⼀段指令,都是存在代码段的,只是虚函
数的地址⼜存到了虚表中。
虚函数表存在哪的?这个问题严格说并没有标准答案C++标准并没有规定,我们写下⾯的代码可以
对⽐验证⼀下。vs下是存在代码段(常量区)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/80170.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【NextPilot日志移植】整体功能概要

整体日志系统的实现功能 该日志系统主要实现了飞行日志的记录功能&#xff0c;支持多种日志记录模式&#xff0c;可将日志存储到文件或通过 MAVLink 协议传输&#xff0c;同时具备日志加密、空间管理、事件记录等功能。具体如下&#xff1a; 日志记录模式&#xff1a;支持按武…

数字化转型:概念性名词浅谈(第二十五讲)

大家好&#xff0c;今天接着介绍数字化转型的概念性名词系列。 &#xff08;1&#xff09;SOP(标准作业程序) 标准作业程序&#xff08;Standard Operating Procedure, SOPs&#xff09;是在有限时间与资源内&#xff0c;为了执行复杂的日常事务所设计的内部程序。从管理学的…

交叉编译 opencv-4.10

编译说明 opencv 下包含很多模块&#xff0c;各个模块的作用可以参考Opencv—模块概览. 嵌入式考虑有限存储等因素会对模块进行裁剪&#xff0c;我这里主要保留图像拼接&#xff08;stitching&#xff09;图片编解码&#xff08;imgcodecs&#xff09;与特征点匹配&#xff08…

Python cv2对象检测与跟踪:从基础到进阶实战

在计算机视觉领域&#xff0c;对象检测&#xff08;定位目标位置&#xff09;与对象跟踪&#xff08;持续追踪目标运动&#xff09;是视频分析、自动驾驶、智能监控等应用的核心技术。本文将结合OpenCV的cv2库&#xff0c;系统讲解其原理与Python实现方法。 一、对象检测 vs 对…

亚马逊推出新型仓储机器人 Vulcan:具备“触觉”但不会取代人类工人

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

缓存套餐-03.功能测试

一.功能测试 点击小程序&#xff0c;就会触发根据分类id查询套餐方法&#xff0c;根据分类id查询套餐。 第一次查询&#xff0c;redis中没有数据&#xff0c;就会发sql进行sql数据库查询。 redis当中就有了对应的缓存。 再次点击&#xff0c;发现sql根本没有执行&#xff0c;…

WebFlux与HttpStreamable关系解析

1-Streamable 1-WebFlux与HttpStreamable关系解析2-MCP协议Streamable HTTP 2-参考网址 MCP协议Streamable HTTPMCP协议重大升级&#xff0c;Spring AI Alibaba联合Higress发布业界首个Streamable HTTP实现方案 3-WebFlux与HttpStreamable关系解析 WebFlux 和 HttpStreamabl…

顺丰科技:从 Presto 到 Doris 湖仓构架升级,提速 3 倍,降本 48%

导读&#xff1a;顺丰科技引入 Doris 替换 Presto&#xff0c;在内部可视化数据自助分析工具丰景台场景广泛应用。目前&#xff0c;顺丰临时查询业务、丰景台报表业务的 Presto 场景已经 100% 切换到 Doris 集群中&#xff0c;日均查询量 100W。并实现 P95 性能提升近 3 倍&…

如何在Jmeter中调用C程序?

在JMeter中调用C语言程序可以通过以下几种方式实现&#xff1a; 方法一&#xff1a;使用OS Process Sampler JMeter的“OS Process Sampler”可以用来调用外部程序&#xff0c;包括C语言编写的可执行文件。 步骤&#xff1a; 准备C语言程序&#xff1a; 编写C语言代码并编译…

python 中的单例

在 Python 里&#xff0c;单例模式指的是一个类仅有一个实例&#xff0c;并且提供一个全局访问点来获取该实例。下面为你介绍几种实现单例模式的常见方法。 1. 使用模块 在 Python 里&#xff0c;模块天然就是单例模式。当模块被导入时&#xff0c;Python 会对其进行一次加载…

Linux58 ssh服务配置 jumpserver 测试双网卡 为何不能ping通ip地址

判断为NAT模式网卡 能ping 通外网 ens34为仅主机模式网卡 [rootlocalhost network-scripts]# ip route show default default via 10.1.1.254 dev ens33 proto static metric 100 10.0.0.0/8 dev ens33 proto kernel scope link src 10.1.1.37 metric 100 11.0.0.0/8 dev…

web 自动化之 selenium+webdriver 环境搭建及原理讲解

文章目录 一、web 自动化测试学习说明二、什么 web 自动化测试三、selenium 简介四、web自动化测试环境搭建五、web 自动化测试第一个脚本六、selenium 原理及源码讲解 一、web 自动化测试学习说明 进阶 web 自动化测试学习&#xff1a;掌握 python 编程基础 二、什么 web 自…

Linux 网络命名空间:从内核资源管理到容器网络隔离

1. 网络命名空间是什么? 网络命名空间(Network Namespace) 是 Linux 内核提供的一种网络资源隔离机制,用于为进程或容器创建完全独立的网络环境。它并非物理或虚拟的网络接口(如网卡、veth pair 等),而是一个虚拟容器,包含以下资源的独立实例: 网络接口(物理或虚拟)…

SQL知识点总结

总结的知识点主要来源于前段时间在牛客刷SQL题目中遇到的错误 目录 1.WHERE字句不能与高级函数连用 2.去重——distinct 3.不等于某个值 4.查多个范围内的值 5. 升/降序排序 6.占位符 7.统计某类别总数计算平均值 8.合并查询——UNION &#xff08;ALL&#xff09; 9…

【软考-高级】【信息系统项目管理师】【论文基础】采购管理过程输入输出及工具技术的使用方法

采购管理概念 项目采购管理包括从项目团队外部采购或获取所需产品、服务或成果的各个过程。项目采购管理包括编制和管理协议所需的管理和控制过程&#xff0c;例如合同、订购单、协议备忘录&#xff08;MOA&#xff09;和服务水平协议&#xff08;SLA&#xff09;。 采购管理…

C++ 手写一个内存池

内存池是一种内存管理技术&#xff0c;它预先分配一大块内存&#xff0c;之后将其按需分割成多个小块供程序使用。下面将详细阐述它的好处以及适用场景。 内存池的好处 减少内存碎片&#xff1a;在动态内存分配时&#xff0c;频繁地分配和释放不同大小的内存块&#xff0c;会…

LeetCode 3341.到达最后一个房间的最少时间 I:Dijkstra算法(类似深搜)-简短清晰的话描述

【LetMeFly】3341.到达最后一个房间的最少时间 I&#xff1a;Dijkstra算法&#xff08;类似深搜&#xff09;-简短清晰的话描述 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-minimum-time-to-reach-last-room-i/ 有一个地窖&#xff0c;地窖中有 n x m 个房间…

学习Linux的第四天

今天我们来学习Linux的网络配置&#xff0c;以及链表的知识开个小头 三种网络配置模式 桥接模式&#xff08;用的最多&#xff09; 2.Nat模式 3. 仅主机模式&#xff08;Nat模式的功能外&#xff0c;只能在局域网通信&#xff0c;不能访问外网&#xff09; 桥接模式&#xf…

【 window.addEventListener(‘message‘, handleMessage)无效的问题】

在react native加载中可能出现 window.addEventListener(‘message’, handleMessage)无效&#xff0c;无法监听到在react-native-webview中通过postMessage发送的消息&#xff0c;可以通过下面的方法来处理 window.addEventListener(message, handleMessage);document.addEven…

css识别\n换行

在CSS中&#xff0c;\n 通常不会被识别为换行符。如果你希望在CSS中实现换行效果&#xff0c;可以使用以下几种方法&#xff1a; 使用 white-space 属性&#xff1a; 设置 white-space: pre 或 white-space: pre-wrap&#xff0c;这样文本中的换行符 \n 会被保留并显示为换行。…