C++程序设计——多态:虚函数、抽象类、虚函数表

news2024/11/29 2:34:14

注:以下示例均是在VS2019环境下

一、多态的概念

        通俗来讲,多态就是多种形态,当不同的对象去完成某个行为时,会产生出不同的状态。即不同继承关系的类对象,去调用同一函数时,产生不同的行为。

        比如”叫“这个行为,不同的动物,发出的声音是不同的。

二、多态的定义及实现

1.多态的构成条件

(1)必须是在继承环境下。

(2)被调用的函数必须是虚函数,且派生类中必须对该虚函数进行重写。

(3)必须通过基类的指针或引用去调用虚函数。

2.虚函数

        被virtual修饰的类成员函数。

3.虚函数的重写

3.1虚函数的重写(覆盖)

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

注意:

(1) 在重写基类虚函数时,派生类的虚函数不加virtual关键字修饰时,也可以构成重写(因为基类的虚函数被继承下来了,在派生类中依旧保持虚函数属性),但是这种写法不太规范,不建议这样使用。

(2)基类和子类虚函数的访问权限可以不同,但是一般都会将基类的虚函数设置为public。

3.2虚函数重写的两个例外

(1)协变(基类与派生类虚函数返回值类型不同)

        派生类重写基类虚函数时,与基类虚函数的返回值类型不同。即基类虚函数返回基类对象的指针或引用,派生类虚函数返回派生类对象的指针或引用时,称为协变

注意:

        基类和派生类必须属于同一继承体系;基类的返回值和派生类的返回值必须属于同一继承体系。但是返回值和基类、派生类不一定属于同一继承体系。

(2)析构函数的重写(基类与派生类析构函数名不同)

        如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否使用virtual关键字修饰,都与基类析构函数构成重写,但是析构函数名不同。

        函数名不同,看起来似乎违背了重写的规则,单其实是编译器对析构函数名做了特殊处理,编译后析构函数名称统一处理成了destructor。

4.C++11——override和final

        在某些情况下,由于疏忽可能会导致函数名字不同而无法构成重写,而这种错误在编译期间是不会报错的,只有在程序运行时没有得到预期结果才通过调试寻找错误,代价较高。

        因此C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

4.1override关键字

        检查派生类虚函数是否重写了基类某个虚函数,若没有则编译报错。

4.2final关键字

        只能修饰虚函数。

        修饰虚函数,表示该虚函数不能再被重写。

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

注意:

(1)重写和重定义必须在继承体系内。

(2)重写只能是成员函数,而且是重写基类的虚函数;重定义既可以是成员函数,也可以是成员变量。

三、抽象类

1.抽象类概念

(1)在虚函数后面加上=0,则该函数为纯虚函数。

(2)包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化对象。

(3)派生类继承抽象类后,也不能直接实例化对象,必须对基类所有纯虚函数进行重写后才能实例化对象,否则派生类也是抽象类。

(4)纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口的继承。

注意:纯虚函数可以不用写函数体,写了不影响但没有意义。

2.接口继承和实现继承

2.1实现继承

        普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。

2.2接口继承

        虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。

        所有若不需要实现多态,不要把成员函数定义为虚函数。

四、多态的原理

1.虚函数表

只要类中存在虚函数,对象大小就会多4个字节:

 

        该指针指向的是一段连续的空间,即虚函数表,里面存放的是虚函数入口地址,且顺序与定义顺序相同。

对象模型:

基类虚表:

(1)虚函数入口地址存放顺序与虚函数定义顺序相同。

(2)一个类的多个对象公用同一个虚表

2.虚函数与虚表存放位置

(1)虚函数和普通函数一样存放在代码段,虚表存放的是虚函数指针。

(2)对象中存放的是虚表指针,不是虚表。

(3)在vs环境下,虚表是存放在代码段的。

3.静态多态与动态多态

3.1静态多态

        在程序编译阶段,就已经确定了程序的行为,也叫静态绑定、前期绑定(早绑定),比如:函数重载、模板。

3.2动态多态

        在程序运行时,根据程序拿到的具体类型确定程序的具体行为,调用具体的函数,比如根据基类的指针或引用指向不同类的对象,选择对应的虚函数进行调用。也叫动态绑定、后期绑定(晚绑定)。

五、单、多继承中的虚函数表

5.1单继承中的虚函数表

基类虚表:

(1)按虚函数声明顺序存放入口地址。

(2)一个类的多个对象公用同一个虚表

子类虚表:

(1)子类有自己独立的虚表,不与父类共用。

(2)子类会将基类虚表拷贝一份放入自己的虚表中。

(3)如果子类重写了基类的某个虚函数,就会用子类自己虚函数的地址去覆盖虚表中被重写的基类虚函数的地址。

(4)子类自己定义的虚函数,其地址入口会按声明顺序依次存放在虚表的末尾。

5.2多继承中的虚函数表

基类虚表:

(1)按虚函数声明顺序存放入口地址。

(2)一个类的多个对象公用同一个虚表

子类虚表:

(1)子类会将基类虚表拷贝一份放入自己的虚表中。

(2)如果子类重写了基类的某个虚函数,就会用子类自己虚函数的地址去覆盖虚表中被重写的基类虚函数的地址。

(3)子类自己新增的虚函数,会将其入口地址添加在第一张虚表之后,第一张虚表即子类第一个继承的基类所拷贝的虚表。

六、常见问题

1.什么是多态?

2.什么是重载、重写、重定义?

3.多态的实现原理?

        虚表的构造?

虚函数的调用原理:

(1)获取对象虚函数表指针(对象前4个字节)

(2)传递this指针

(3)从虚表中找到对应虚函数的入口地址

(4)调用该虚函数

4.inline函数可以是虚函数吗?

        语法上可以。不过编译器会忽略inline属性,这个函数就不再是inline,因为虚函数的地址入口需要放入到虚表中去。

5.静态成员可以是虚函数吗?

        不可以。因为静态成员函数没有this指针,使用“类名::成员函数”的调用方式无法访问虚函数表,所以静态成员不能放入虚函数表。

6.构造函数可以是虚函数吗?

        不可以。因为对象的虚函数表指针是在构造函数初始化列表阶段才初始化的。

7.析构函数可以是虚函数吗?什么场景是?

        可以。最好是把基类的析构函数定义为虚函数。场景:在继承体系中,基类的析构函数最好设置为虚函数,防止用基类的指针去销毁派生类对象时,只调用基类的析构函数而不调用子类的析构函数。

8.对象访问普通函数快还是虚函数快?

        若是普通对象,则一样快;若是指针对象或引用对象,则调用普通函数快。因为通过指针或引用访问虚函数时,需要在运行过程中去查询虚表才能确定函数入口地址,而普通对象在编译时就已经确定了函数的入口地址。

9.虚函数表是在哪个阶段生成的,存放在哪儿?

        虚函数表在编译阶段生成,一般情况存放在代码段(常量区)。

10.菱形继承的问题?虚拟原理?

        注意不要混淆虚函数表和虚基表。

11.什么是抽象类?抽象类的作用?

……

作用:抽象类规范了派生类必须重写纯虚函数,另外纯虚函数更体现出了接口的继承。

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

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

相关文章

【React】course

《千锋HTML5实战就业班React课程体系V16版本》课程资料 一、关于React 英文官网:https://reactjs.org/ 中文官网:https://zh-hans.reactjs.org/ React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架&#xff0c…

JAVA工具篇--1 Idea中 Gradle的使用

前言: 既然我们已经使用Maven 来完成对项目的构建,为什么还要使用Gradle 进行项目的构建;gradle和maven都可以作为java程序的构建工具,但两者还是有很大的不同之处的:1.可扩展性,gradle比较灵活&#xff0c…

RabbitMQ-消息应答

一、介绍为了保证消息在发送过程中不丢失,rabbitmq引入消息应答机制,消息应答就是:消费者在接收到消息并且处理该消息之后,告诉rabbitmq它已经处理了,rabbitmq可以把该消息删除了。二、自动应答消息发送之后立即被认为…

pytest-fixture

fixture是pytest特有的功能,它用pytest.fixture标识,定义在函数前面。在编写测试函数的时候,可以将此函数的名称作为传入参数,pytest会以依赖注入方式将该函数的返回值作为测试函数的传入参数。fixture主要的目的是为了提供一种可…

从C语言向C++过渡

文章目录前言1.命名空间1.域的概念2.命名空间的使用2.C输入&输出3.缺省参数1.概念2.分类3.注意事项4.函数重载5.引用1.概念2.使用注意事项3.引用使用场景4.指针和引用的区别6.内联函数7.auto关键字8.nullptr前言 C被成为带类的C,本文由C语言向C过度,将会初步介…

电子器件系列32:逻辑与门芯片74LS11

一、编码规则 先看看这个代码的意思:74LS11 74是一个系列(74 表示为工作温度范围,74: 0 ~ 70度。) ls的意思就是工艺类型(Bipolar(双极)工艺) 11是代码 什么是74系列逻辑芯片? - 知乎 什么是…

【MyBatis】第八篇:一级,二级缓存

其实缓存字面的意思就是将一些内容缓存下来,等下次使用的时候可以直接调用,通过数据库得到数据,有时候会使用相同的数据,所以mybatis自然也支持缓存。 而mybatis按照缓存的效果可以分两大类:一级缓存和二级缓存。 一…

node.js下载和vite项目创建以及可能遇到的错误

目录 一、node.js的下载 1、去官网下载 节点.js (nodejs.org) 2、下载过程 第一步: 第二步: 第三步: 第四步: 第五步: 二、vite项目的创建(使用的工具是Hbuilder x) 第一步: 出现报错…

基于matlab多功能相控阵雷达资源管理的服务质量优化

一、前言此示例说明如何为基于服务质量 (QoS) 优化的多功能相控阵雷达 (MPAR) 监控设置资源管理方案。它首先定义必须同时调查的多个搜索扇区的参数。然后,它介绍了累积检测范围作为搜索质量的度量,并展示了…

低代码开发平台|制造管理-工艺工序搭建指南

1、简介1.1、案例简介本文将介绍,如何搭建制造管理-工艺工序。1.2、应用场景先填充工序信息,再设置工艺路线对应的工序;工序信息及工艺路线列表报表展示的是所有工序、工艺路线信息,可进行新增对应数据的操作。2、设置方法2.1、表…

权限管理实现的两种方式(详解)

登录的接口请求的三个内容:1. token2. 用户信息、角色信息3. 菜单信息第一种:基于角色Role的动态路由管理 (不推荐,但市场用的比较多)首先列出枚举每个角色对应几个路由,然后根据用户登录的角色遍历枚举出来的角色动态注册对应的路…

4年功能测试经验,裸辞后找不到工作怎么办?

软件测试四年,主要是手动测试(部分自动化测试和性能测试,但是用的是公司内部自动化工具,而且我自动化方面是弱项。) 现在裸辞三个月了,面试机会少而且面试屡屡受挫。总结就是自动化,性能&#…

微服务保护:一、初识Sentinel

1.1.雪崩问题及解决方案 1.1.1.雪崩问题 微服务中,服务间调用关系错综复杂,一个微服务往往依赖于多个其它微服务。 如图,如果服务提供者I发生了故障,当前的应用的部分业务因为依赖于服务I,因此也会被阻塞。此时&…

链接脚本学习笔记

IAR 一般步骤 链接器用于链接过程。它通常执行以下过程(请注意,某些步骤可以通过命令行选项或链接器配置文件中的指令关闭): 1.确定应用程序中要包含哪些模块。始终包含对象文件中提供的模块。仅当库文件中的模块为从包含的模块…

智慧楼宇系统:商办楼宇运营方的不二之选

现在,许多商办楼宇运营方都倾向于选择专业的商办楼宇管理系统来完成日常运营管理,从而实现楼宇的高效运作。 那么问题来了,商办楼宇运营者该如何选购一个合适的楼宇管理系统?在选择系统之前,必须要清楚系统能解决哪些…

NODE => CORS跨域资源共享学习

1.CORS跨域资源共享 cors是Express的一个第三方中间件。通过安装和配置cors中间件,可以很方便地解决跨域问题 运行npm install cors 安装中间件使用const cors require(‘cors’) 导入中间件在路由之前调用 app.use(cors()&#…

Java · 常量介绍 · 变量类型转换 · 理解数值提升 · int 和 Stirng 之间的相互转换

书接上回 Java 变量介绍 我们继续学习以下内容. 四、常量字面值常量final 关键字修饰的常量五、理解类型转换int 和 long/double 相互赋值int 和 boolean 相互赋值int 字面值常量给 byte 赋值强制类型转换类型转换小结六、理解数值提升int 和 long 混合运算byte 和 byte 的运算…

大数据未来会如何发展

大数据应用的重要性,自全国提出“数据中国”的概念以来,我们周围默默地在发挥作用的大数据逐渐深入人们的心中,大数据的应用也越来越广泛,具体到金融、汽车、餐饮、电信、能源、体育和娱乐等领域 为什么大数据技术那么火&#xf…

机器学习之决策树原理详解、公式推导(手推)、面试问题、简单实例(python实现,sklearn调包)

目录1. 决策树原理1.1. 特性1.2. 思路1.3. 概念决策树概念信息论2. 公式推导2.1. 构造决策树2.1.1. ID3理论示例缺点2.1.2. C4.5理论示例缺点2.1.3. CART示例对比分析2.2. 剪枝3. 实例3.1. 数据集3.2. ID33.3. C4.53.4. CART3.5. sklearn实现4. 几个注意点(面试问题)5. 运行&am…

高性能MySQL -- 查询性能优化

一般来说一个好的程序:查询优化,索引优化,库表结构要同时进行优化。今天我们来讲一下查询优化。 我们需要对MySQL的架构有基本认知,所以这里贴一张图大家看看: 图片来自于《小林coding》 为什么从查询会慢&#xff1…