c++的多态
目录
1、多态
1.1多态的构成条件
1.2多态的好处
2、虚函数
2.1虚函数重写
2.2虚函数的默认参数
2.3纯虚函数重写
2.4抽象类
2.5虚析构,纯虚析构重写
3、重载、覆盖(重写)、隐藏(重定义)的对比
编辑
多态是c++面向对象三大特性之一
程序调用函数时,将使用哪个可执行代码块呢?编译器负责回答这个问题。将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编。在c语言中,在非常简单,因为每个函数名都对应一个不同的函数。在c++中,由于函数重载的缘故,这项任务更复杂。编译器必须查看函数参数以及函数名才能确定使用哪个函数。然而,c/c++编译可以在编译过程完成这种联编。在编译过程中进行联编被称为给静态联编,又称为早期联编,然而虚函数使这项工作变得更困难。编译器必须生成能够在程序运行时选择正确使虚方法的代码,这被称为动态联编,又称为晚期联编。
1、多态
多态就是函数调用的多种形态,使用多态能够使得不同的对象去完成同一件事时,产生不同的动作和结果。
多态分为静态多态和动态多态
(1)静态多态,也成为静态绑定或前期绑定(早绑定):函数重载和运算符重载就属于静态多态。静态多态也成为编译期间的多态,编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果又对应的函数就调用该甘薯,否则出现编译错误
(2)动态多态,也成为动态绑定或后期绑定(晚绑定):在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,即运行时的多态。在程序执行期间(非编译期)判断所英勇的实际类型,根据其实际类型调用相应的方法。
父类指针或引用指向父类,调用的就是父类的虚函数
父类指针或引用指向子类,调用的就是子类的虚函数
静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
1.1多态的构成条件
同一操作作用于不同的对象,可以有不同的执行结果,产生不同的执行结果,这就是多态性。简单的说,就是用基类的指针指向子类的对象
所以在继承中要想构成多态需要满足两个条件:
1、必须通过基类的指针或者引用指向子类的对象
2、被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
1.2多态的好处
多态的优点:
1、代码组织结构清晰
2、可读性强
3、利于前期和后期的扩展以及维护
2、虚函数
被virtual修饰的类成员函数被称作虚函数
1、只有类的非静态成员函数前可以加virtual,普通函数前不能加virtual
2、虚函数的virtual和虚继承的vittual是同一个关键字,但实际上并没有任何关系。虚函数的virtual是为了实现多态,而虚继承的virtual是为了解决菱形继承的数据冗余和二义性
2.1虚函数重写
虚函数的重写也叫做虚函数的覆盖,若派生类中有一个和基类完全相同的虚函数(返回值类型相同,函数名相同以及参数列表完全相同),这种称为派生类的虚函数重写了基类的虚函数。
通过基类的指针或引用子类对象,从而调用我们写的虚函数,此时根据不同类型的对象,调用的就是不同的函数,产生的也是不同的结果,进而实现了动态多态。
调用哪个类型的虚函数,取决于基类指针指向或引用的对象是哪种类型的对象。
如果不使用基类的指针或引用去调用虚函数,则只会调用基类的虚函数。
#include <iostream>
using namespace std;class Animal
{
public://Speak函数就是虚函数//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。virtual void speak(){cout << "动物在说话" << endl;}
};class Cat :public Animal
{
public:void speak(){cout << "小猫在说话" << endl;}
};class Dog :public Animal
{
public:void speak(){cout << "小狗在说话" << endl;}};void DoSpeak(Animal & animal)
{animal.speak();
}void test01()
{Cat cat;DoSpeak(cat);Dog dog;DoSpeak(dog);
}int main() {test01();return 0;
}

注意:在重写基类虚函数时,派生类的虚函数不加virtual关键字也可以进行动态多态,主要原因是因为继承后基类的虚函数被继承下来了,在派生类中依旧保持虚函数属性。但是这种写法不是很规范,因为派生类也有可能会被继承,为了区分虚函数,建议在派生类的虚函数前也加上virtual关键字。
2.2虚函数的默认参数
动态多态中,虚函数默认参数,调用者是哪个类,就用对应类中的函数的默认参数,如果不是使用默认参数,那么就会用传递进去的参数
参数值的优先顺序:传递进去的参数 > 基类的默认参数 > 派生的默认参数
测试代码如下:
#include <iostream>
using namespace std;class Base
{
public:Base(){cout<<"Base()"<<endl;}~Base(){cout<<"~Base()"<<endl;}virtual void show(int a=123) //基类的虚函数带默认参数{cout<<"Base show"<<a<<endl;}
private:
};//派生类
class Child:public Base{
public:Child(){cout<<"Child()"<<endl;}~Child(){cout<<"~Child()"<<endl;}void show(int a=456) //派生类也带默认参数{cout<<"Child show"<<a<<endl;}
private:
};int main()
{//派生类访问自己的成员 不是多态Child mya;//mya.show();//Child show456//多态中,虚函数默认参数,调用者是哪个类,就用对应类中的函数的默认参数Base *d = &mya;//Child show123d->show();//如果不是使用默认参数,那么就会用传递进去的参数//参数值得优先顺序 传递进去的参数 > 基类的默认参数 >派生类的默认参数// d->show(1000);return 0;
}
2.3纯虚函数重写
在堕胎中,通常父类的虚函数的实现是某无意义的,主要都是调用了子类重写的内容,那我们就可以将虚函数写成纯虚函数。c++通过使用纯虚函数提供未实现的函数。
纯虚函数的格式:
virtual 函数返回类型 函数名(参数表) = 0;
在虚函数声明的时候直接赋值为0,这样虚函数就变成纯虚函数了
什么时候下使用纯虚函数
在基类本身生成对象时不合理的时候,比如动物作为一个基类派生出老虎等子类,但动物本身这基类直接生成对象时不合理的,所以为解决这问题,方便使用类的多态性,引入了纯虚函数的概念,将函数定义为纯虚函数,则编译器要求在派生类中必须要重新给纯虚函数以实现多态性
纯虚函数并不需要实现,如果一个类中有纯虚函数那么这个类就是抽象类,如果派生类没有把基类的纯虚函数全部实现,那么派生类还是抽象类。
2.4抽象类
在类中包含纯虚函数,那么一个类中有纯虚函数那么这个类就是抽象类
抽象类的特点:
1、无法实例化对象
2、子类必须重写抽象类中的纯虚函数,否则也属于抽象类
实现继承: 普通函数的继承是一种实现继承,派生类继承了基类函数的实现,可以使用该函数。
接口继承: 虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态。
建议: 所以如果不实现多态,就不要把函数定义成虚函数。
2.5虚析构,纯虚析构重写
我们有时会让一个基类指针指向用 new 运算符动态生成的派生类对象;同时,用 new 运算符动态生成的对象都是通过 delete 指向它的指针来释放的。如果一个基类指针指向用 new 运算符动态生成的派生类对象,而释放该对象时是通过释放该基类指针来完成的,就会导致delete的时候只会调用基类的析构函数,而不会调用派生类的析构函数,如果派生类的析构函数中有释放成员内存空间,可能会造成内存泄漏。所以C++ 规定,需要将基类的析构函数声明为虚函数,即虚析构函数。只要基类的析构函数是虚函数,那么派生类的析构函数不论是否用virtual关键字声明,都自动成为虚析构函数。一般来说,一个类如果定义了虚函数,则最好将析构函数也定义成虚函数。
为什么要需要虚析构函数呢?
动态多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码,会造成内存泄漏,解决这个问题就需要用到虚析构函数。
虚析构函数的格式:
virtual ~类名(){}
1、虚析构函数就是用来解决通过父类指针释放子类对象。
2、如果子类中没有堆区数据,可以不写虚析构函数
纯虚函数的格式
virtual ~类名() = 0; //类内定义
类名::~类名(){}` //类外声明
和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是抽象类,不能被实例化,测试代码如下:
#include<iostream>
#include <unistd.h>
#include<cstring>
using namespace std;//基类 --动物类
class Animal{
public:Animal(){cout<<"Animal()"<<endl;}//将基类的析构函数声明为 虚析构函数//作用:当使用基类的指针销毁 派生类对象的时候,让派生类对象的析构函数也执行virtual ~Animal(){cout<<"~Animal()"<<endl;} //常用方法---派生类里面是否有指针//可以把基类的虚析构函数 声明定义为 纯虚析构函数//纯虚析构 函数必须在类内声明 类外实现 //virtual ~Animal() = 0;//~Animal();//行为//如何让基类 不能实例化对象//有两种方法:第一种 将虚函数声明定义为纯虚函数// 第二种方法将虚析构函数声明定义为纯析构函数,但是纯析构函数必须在类外实现// virtual void speak(){// cout<<"Animal::speak"<<endl;// }virtual void speak() = 0;};// Animal::~Animal()
// {
// cout<<"~Animal()"<<endl;
// }//派生类 狗
class Dog:public Animal
{
public:Dog(const char*name = "旺财"){cout<<"Dog()"<<endl;d_name = new char[strlen(name) + 1];strcpy(this->d_name,name);}//当基类的析构函数声明为 虚析构的时候,派生类的析构函数也默认会加上关键字 virtual// ~Dog()virtual ~Dog(){cout<<"~Dog()"<<endl;//在析构函数中 释放 指针成员 指向的堆空间delete []this->d_name;}//派生类 中 实现 基类的 虚函数 virtual void speak(){cout<<"Dog::speak"<<endl;}
private:char *d_name;
};int main()
{//Animal *p = new Dog;//通过基类指针指向 派生类对象//p->speak();//通过基类指针 释放 对象的内存空间,默认只会调用 基类的析构函数//delete p; //虚拟析构不仅释放了基类的new空间,也释放了派生类的new空间//动物类 是 基类 ,实例化 对象 不合理 //因为基类中有纯虚函数(纯虚析构函数),所以该类是抽象类,抽象类不能实例化//抽象类:只能通过继承 在子类中 重写纯虚函数Animal p1;//p1.speak();return 0;
}
//将基类的析构函数声明为 虚析构函数
/作用:当使用基类的指针销毁 派生类对象的时候,让派生类对象的析构函数也执行
//可以把基类的虚析构函数 声明定义为 纯虚析构函
//纯虚析构 函数必须在类内声明 类外实现
//如何让基类 不能实例化对象
//有两种方法:第一种 将虚函数声明定义为纯虚函数
// 第二种方法将虚析构函数声明定义为纯析构函数,但是纯析构函数必须在类外实现
3、重载、覆盖(重写)、隐藏(重定义)的对比
相关文章:
c++的多态
目录 1、多态 1.1多态的构成条件 1.2多态的好处 2、虚函数 2.1虚函数重写 2.2虚函数的默认参数 2.3纯虚函数重写 2.4抽象类 2.5虚析构,纯虚析构重写 3、重载、覆盖(重写)、隐藏(重定义)的对比 编辑 多态是c面向对象三大特性之一 程序调用函数时&#…...
【数据结构与算法】堆的实现(附源码)
目录 一.堆的概念及结构 二.接口实现 A.初始化 Heapinit 销毁 Heapdestroy B.插入 Heappush 向上调整 AdjustUp 1.Heappush 2.AdjustUp C.删除 Heappop 向下调整 AdjustDown D.堆的判空 Heapempty 堆顶数据 Heaptop 堆的大小 Heapsize 三.源码 Heap.h He…...
win10彻底永久关闭自动更新【亲测有效】
一、禁用Windows Update服务 1、同时按下键盘 Win R,打开运行对话框,然后输入命令 services.msc ,点击下方的“确定”打开服务,如下图所示。 2、找到 Windows Update 这一项,并双击打开,如图所示。 3、右击…...
【Unity UPR】造个获取深度法线纹理的轮子
描边需要深度法线纹理的加持,效果才能达到最好,但URP下很多版本不支持直接获取_CameraNormalsTexture,而我本人也尝试了一下在12.1.7下偷懒直接拿SSAO里的Depth Normal图, 虽然也能实现吧,但是需要打开SSAO的同时&…...
用 Python解析HTML页面
用 Python 解析 HTML 页面 在网络爬取的过程中,我们通常需要对所爬取的页面进行解析,从中提取我们需要的数据。网页的结构通常是由 HTML 标签所组成的,通过对这些标签的解析,可以得到网页中所包含的有用信息。在 Python 中&#…...
python logging 详解
python logging 详解1. 导入logging模块2. 配置日志记录器3. 记录日志消息4. 自定义日志记录器5. 日志轮换6. 日志过滤器7. 日志异常跟踪8. 日志输出到控制台和文件9. 使用配置文件10. 使用第三方库11. format格式详解12. 总结Python的logging模块提供了灵活的日志记录功能&…...
( “树” 之 DFS) 687. 最长同值路径 ——【Leetcode每日一题】
687. 最长同值路径 给定一个二叉树的 root ,返回 最长的路径的长度 ,这个路径中的 每个节点具有相同值 。 这条路径可以经过也可以不经过根节点。 两个节点之间的路径长度 由它们之间的边数表示。 示例 1: 输入:root [5,4,5,1,1,5] 输出&…...
Elasticsearch解决不能修改索引、字段问题解决方案
问题1: 由于es索引不能删除,不能修改,在不影响原数据的情况下,并且生产服务不停机的情况下,怎么修改索引,并保留原索引内的数据? 基于kibanna的dev Tools执行参数,淘汰postman&…...
面试官在线改简历 | 只有6秒!程序员简历这样写才能抓住科技公司大佬的眼球
其实每一份简历 每一个瑞库特 可能也就平均花6秒钟的时间看一看 来进行一个快速的筛选 一份好的简历到底应该长什么样 同时呢在我们写简历的过程当中 应该避免什么样子的错误和误区 那我们今天呢来聊聊这个简历的事 大家知道 每次到了招聘高分期啊这些大的公司 像谷歌Facebook…...
IM即时通讯-7-如何设计通知提醒
本文大纲 本文从为什么做通知提醒, 以及如何设计通知提醒, 以及如何衡量通知提醒三方面解释了如何设计通知提醒。 对于重点的如何设计通知提醒, 通过拆分前台和后台, 前台采用自建或者二方通道, 后台采用厂商信令通道…...
赛狐ERP | 亚马逊选品方法与策略详解:如何挑选最优质的产品?
亚马逊作为全球电商巨头,其产品种类之丰富也是无人能及。然而,在如此繁杂的商品体系下,如何选品成为了摆在商家面前的一道难题。本文将从亚马逊选品的目标、方法、策略三个方面进行详细介绍。 一、选品的目标 在进行选择之前,必…...
【GCU体验】基于PyTorch + GCU跑通ResNet50模型并测试GCU性能
一、环境 地址:启智社区:https://openi.pcl.ac.cn/ 二、计算卡介绍 云燧T20是基于邃思2.0芯片打造的面向数据中心的第二代人工智能训练加速卡,具有模型覆盖面广、性能强、软件生态开放等特点,可支持多种人工智能训练场景。同时具备灵活的可…...
【机器视觉------标定篇(二)】三点成圆算法(求相机旋转中心)
应用场景 机器视觉项目应用中,相机安装在机器人上,并且需要定位产品返回坐标偏差以及角度偏差。 与九点标定配合使用,实现精准角度补偿。 算法输入 不共线的三点坐标 A(X₁,Y₁) ,B(X₂,Y₂&…...
AUTOSAR E2E详细介绍
E2E概述 E2E(End-To-End)是AUTOSAR为功能安全ISO26262提出的一个安全模块。这里的端(End)并不是指ECU与ECU之间,而是指通信ECU上的SW-C与SW-C之间。 在车载网络中,信息交换通常是从一个ECU发送信号,另一个ECU接收信号。对E2E而言,通常是从源SW-C生成信号,经过RTE(R…...
Dream 主题使用手册 - 基础篇
Dream 主题基于 Halo 博客系统开发,本文将介绍本主题一些功能的使用,文档将持续更新。 一、安装 & 更新 1.1 安装包安装 & 更新 进入主题 Release 界面:https://github.com/nineya/halo-theme-dream/releases 下载主题压缩包 halo…...
WSL下的Kafka开发容器:Docker搭建、API、整合
背景介绍 Kafka是一个分布式流处理平台,可以处理大规模数据流并支持实时数据流的处理。 本文介绍了如何在WSL下使用Docker搭建Kafka容器,并使用Python的kafka-python库和FastAPI框架实现了一个简单的API。同时,还将该服务整合到一个整体的d…...
cv2(OpenCV)下载安装
cv2对应库是OpenCV,官网下载链接:https://www.lfd.uci.edu/~gohlke/pythonlibs/#opencv 最好下载对应python版本的,通过pip命令安装可能会出现版本过高或者过低的问题,导致import cv2没问题,但是内部函数无法调用。 …...
【剑指 offer】旋转数组的最小数字
✨个人主页:bit me👇 ✨当前专栏:算法训练营👇 旋 转 数 组 的 最 小 数 字核心考点:数组理解,二分查找,临界条件 描述: 有一个长度为 n 的非降序数组,比如[1,2,3,4,5]…...
GB 9706.1-2020 医用电气设备第1部分:基本安全和基本性能的通用要求-1
这是份什么文件 这是一份中华人民共和国国家标准,具体为GB9706.1—2020,标准适用于医用电气设备,并规定了医用电气设备基本安全和基本性能的通用要求。主要涵盖了医疗电器设备与患者接触的各种要求,包括电气安全、机械防护、防护辐…...
认识C++《共、枚、指1》
目录 前言: 1.共用体的基本知识 2.匿名共用体 3.枚举 3.1设置枚举值 3.2枚举的应用场景 3.3枚举变量的取值范围 4.地址和自由存储空间 5.指针的思想 6.指针的声明和初始化 前言: 指针内容比较多,还需要再出一篇。久等了!!我看了我的…...
Go的interface空值与类型断言的最佳实践
Go语言中的interface空值与类型断言是开发者经常遇到的核心概念,掌握其最佳实践能显著提升代码的健壮性和可维护性。interface的灵活性使其成为Go多态的重要工具,但空值处理和类型断言的不当使用可能导致运行时错误或逻辑漏洞。本文将深入探讨如何高效处…...
S2-Pro创意写作效果展示:多种文体与风格仿写
S2-Pro创意写作效果展示:多种文体与风格仿写 1. 开篇:当AI遇见创意写作 最近试用S2-Pro进行创意写作,结果让我这个老文案都感到惊艳。这款模型不仅能流畅生成各类文体,还能精准模仿名家风格,就像一位全能的文字魔术师…...
实战指南:基于快马生成tomcat生产级配置,涵盖https、集群与性能调优
今天想和大家分享一个实战经验:如何在生产环境中配置Tomcat服务器。作为一个长期和Tomcat打交道的开发者,我深知生产环境配置和本地开发环境的巨大差异。最近在InsCode(快马)平台上实践了一套完整的配置方案,效果很不错,这里把关键…...
Chandra OCR真实测评:对比GPT-4o,开源OCR模型表现如何
Chandra OCR真实测评:对比GPT-4o,开源OCR模型表现如何 最近在整理一堆扫描版的实验报告和学术论文,里面混杂着复杂的表格、手写注释和数学公式,真是让人头疼。传统的OCR工具,比如Tesseract,处理这种文档就…...
弦音墨影保姆级教程:解决‘米色宣纸背景不显示’‘朱砂按钮无响应’等常见问题
弦音墨影保姆级教程:解决‘米色宣纸背景不显示’‘朱砂按钮无响应’等常见问题 1. 引言:优雅水墨AI的实用指南 「弦音墨影」是一款将尖端人工智能技术与中国传统美学深度融合的视频理解与视觉定位系统。它以"水墨丹青"为视觉灵魂,…...
AXI Quad SPI IP核在多主设备环境下的三态总线设计与实现
1. AXI Quad SPI IP核的多主设备挑战 第一次接触AXI Quad SPI IP核的多主设备配置时,我踩过一个典型的坑:两个FPGA内部主模块同时向SPI总线发送数据,导致MOSI信号出现毛刺。这种情况在共享总线架构中非常常见,而三态总线设计正是解…...
资源优化挑战:如何用轻量级字体解决嵌入式系统中文显示难题
资源优化挑战:如何用轻量级字体解决嵌入式系统中文显示难题 【免费下载链接】LxgwWenKai LxgwWenKai: 这是一个开源的中文字体项目,提供了多种版本的字体文件,适用于不同的使用场景,包括屏幕阅读、轻便版、GB规范字形和TC旧字形版…...
面向生产的Chatgpt5.4:系统集成、架构模式与成本优化深度拆解
对于计划将顶级AI能力深度集成至自身产品与工作流的团队而言,理解Gemini 3.1 Pro的系统级特性、集成模式与全生命周期成本至关重要。国内开发者可通过RskAi(www.rsk.cn)等聚合平台,以零成本、国内直访的方式完成前期技术验证与原型…...
颠覆式数据主权革命:WeChatMsg如何让你的聊天记录真正归属自己
颠覆式数据主权革命:WeChatMsg如何让你的聊天记录真正归属自己 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/…...
PHPStudy环境下ThinkPHP8与PHP8.2.9的完美搭配:XDbug与Redis扩展实战指南
1. PHPStudy环境与PHP8.2.9的安装配置 对于本地开发环境来说,PHPStudy一直是我的首选工具。它集成了Apache/Nginx、MySQL和PHP,一键安装就能搞定所有基础服务。最近在做一个新项目,需要用到ThinkPHP8框架,所以决定尝试最新的PHP8.…...
