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.指针的声明和初始化 前言: 指针内容比较多,还需要再出一篇。久等了!!我看了我的…...

vim 一键配置
PS:本文是为了以后为了方便,做备忘的,今天用的时候找了半天很麻烦。 vim编辑器一键配置 在非root用户下执行上面的语句即可,不要在root用户下直接安装! 安装的时候需要输入root用户的密码,请找您的服主要一…...

如何成为一名成功的 PHP 开发者
当今的网络应用开发市场,PHP 一直是其中最受欢迎的语言之一,许多优秀的网络应用程序都是由 PHP 开发人员设计和开发的。如果你想成为一名成功的 PHP 开发者,以下是几个关键步骤: 1. 学习基础知识 首先,你需要掌握 PH…...

UHD安装教程
UHD Universal Hardware Driver,即USRP驱动。 UHD,Windows平台安装教程 uhd驱动安装 http://files.ettus.com/binaries/misc/erllc_uhd_winusb_driver.zip 安装LibUSBx http://files.ettus.com/binaries/uhd/latest_release 下载默认C盘 环境配置 将…...

Unity和UE有啥区别?哪个更适合游戏开发
游戏制作软件中最著名的两个游戏引擎是 Unity 和 Unreal Engine。从独立游戏到大型工作室,许多游戏开发商都在使用它们。如果你打算从事游戏行业工作,你肯定曾经问过自己“我的游戏应该使用 Unity 还是 Unreal Engine?” ” 让我们来了解和比…...

红队内网靶场
文章目录开篇介绍靶场介绍靶场下载以及配置Tomcat Get Shell突破DMZ防火墙拿下域内成员机器将内网机器上线到CS使用Adfind侦察子域信息控制子域DCRadmin登录子域进行权限维持(白银票据/ACL)子域bloodhound获取父域信息分析子域Krbtgt密钥创建跨域金票Dcsync父域PTH父域DC准备打…...

如何合并多个升序链表?
前言 本文主要介绍如何将多个小的升序链表合并一个大的升序链表。 需求描述 给出K个升序链接,要求把这K个升序链表合并成一个,并且这个链表也是升序的。 例如:A [1,5,6], B [2,3,8], C [4,4,9] 将这3个链表合并成一个链表D…...

23上半年信息系统项目管理师新老教程兼顾使用备考策略
在离考试仅有50多天的时候,软考办发文:“为方便报考信息系统项目管理师的考生进行复习备考,2023年上半年信息系统项目管理师考试第3版、第4版教程兼顾使用”。 其实软考办发布这样一条信息,也是为了照顾那些在新版发布以前按第…...

Linux环境搭建SVN服务器并实现公网访问 - cpolar端口映射
文章目录前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6.2 …...

仿牛客网社区Web开发项目代码逐行精读(更新中)
仿牛客网社区Web开发项目怎么看项目?如何调试项目前瞻技术架构项目亮点开始看代码LoginControllerDiscussPostController怎么看项目? pom.xml看技术架构resource看配置文件,这个项目是前后端不分离的以调试为导向,从前端入手检查…...

5G NR调制阶数与EVM关系以及对系统SNR要求分析
移动通信技术对数据传输速率要求越来越高。一种提高传输速率的思路是使用更高阶的QAM 调制方式,例如5G NR 的256QAM PDSCH,微波的1024QAM,2048QAM和4096QAM 调制。更高阶的QAM 调制方式对系统也提出了更高的要求。例如某个系统的EVM 测试结果…...