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

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

注:以下示例均是在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.什么是抽象类?抽象类的作用?

……

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

相关文章:

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

注:以下示例均是在VS2019环境下 一、多态的概念 通俗来讲,多态就是多种形态,当不同的对象去完成某个行为时,会产生出不同的状态。即不同继承关系的类对象,去调用同一函数时,产生不同的行为。 比如”叫“这…...

OpenMMLab AI实战营 第6课 语义分割与MMSegmentation

第6课 语义分割与MMSegmentation 1. 语义分割简介 任务:将图像按照物体的类别分割成不同的区域,等价于对每个像素进行分类应用 无人驾驶人像分割智能遥感医疗影像分析 语义分割 vs 实例分割 vs 全景分割 语义分割:仅考虑像素的类别&#xf…...

产业互联网是对互联网的衍生和进化,也是一次重塑和再造

互联网并不仅仅只是充当撮合和中介的角色,它应当具备更多的功能和意义。只有这样,它的发展才能够真正全面和完善。产业互联网的衍生和出现,正是在互联网进化的基础之上出现的。这是我们看到之所以会有那么多的互联网玩家投身到产业互联网的浪…...

Shell脚本之——Hadoop3单机版安装

目录 1.解压 2.文件重命名 3.配置环境变量 4.hadoop-env.sh 5.core-site.xml 6. hdfs-site.xml 7. mapred-site.xml 8.yarn-site.xml 9.完整脚本代码(注意修改主机名) 10.重启环境变量 11.初始化 12.启动服务 13.jps查询节点 1.解压 tar -zxf /opt/install/hadoo…...

代码随想录NO39 |0-1背包问题理论基础 416.分割等和子集

0-1背包问题理论基础 分割等和子集1. 0-1背包问题理论基础(二维数组实现)2. 0-1背包问题理论基础 二(一维数组实现)1. 0-1背包问题理论基础(二维数组实现) 背包问题一般分为这几种: 0-1背包问题:有n件物品和一个最多能背重量为w…...

FITC-PEG-FA,荧光素-聚乙二醇-叶酸,FA-PEG-FITC,实验室科研试剂,提供质量检测

FITC-PEG-FA,荧光素-聚乙二醇-叶酸 中文名称:荧光素-聚乙二醇-叶酸 英文名称:FITC-PEG-FA 英文别名:Fluorescein-PEG-Folic Acid 性状:基于不同的分子量,呈白色/类白色固体,或粘稠液体。 溶…...

简洁易懂:源码+实战讲解Redisson并发锁及看门狗自动续期

1 缘起 有一次同事问Redisson存储的键是否为hash? 我当时,没有看Redisson的相关源码,只知道应用, 所以没有办法回答,于是开始看看Redisson实现的源码, 顺便写了一个单机Redisson测试, 发现Redi…...

TCP 三次握手和四次挥手

✏️作者:银河罐头 📋系列专栏:JavaEE 🌲“种一棵树最好的时间是十年前,其次是现在” 目录TCP 建立连接(三次握手)为啥不能是 4 次?为啥不能是 2 次?三次握手的意义:TCP 断开连接(四…...

JavaWeb复习

JavaWeb复习一.概述1.概念2.B/S和C/S 架构二.HTTP通信协议概述1.概念2.HTTP1.0 与 HTTP1.1 版本3.HTTP 协议组成4.常见状态码5.GET 与 POST 请求方式三.Tomcat1.Web服务器介绍2.安装(Windows)3.Tomcat目录结构4.server.xml部分配置解释四.Servlet1.概念2…...

P14 PyTorch AutoGrad

前言:激活函数与loss的梯度PyTorch 提供了Auto Grad 功能,这里系统讲解一下torch.autograd.grad系统的工作原理,了解graph 结构目录:1: require_grad False2: require_grad True3: 多层bakcward 原理4: in…...

前端报表如何实现无预览打印解决方案或静默打印

在前端开发中,除了将数据呈现后,我们往往需要为用户提供,打印,导出等能力,导出是为了存档或是二次分析,而打印则因为很多单据需要打印出来作为主要的单据来进行下一环节的票据支撑, 而前端打印可…...

Operating System Course 2 - My OS

Computer Startup process上一篇:http://t.csdn.cn/XfUKt 讲到这个启动设备的第一个扇区:引导扇区。那么引导扇区的代码长什么样子?这里得看引导扇区代码源文件bootsect.s(.s后缀文件为用汇编语言编写的源代码文件)。另…...

离散数学 课时一 命题逻辑的基本概念

1 命题 1、命题:可以判断其真值的陈述句 2、真值:真或者假(1或者0) 3、真命题:真值为真的命题 4、假命题:真值为假的命题 5、原子命题:不可以再被分解成更简单的命题 6、复合命题:由原子命题通过联结词联结…...

Word文档带有权限密码怎么办?

Word文档的权限密码指的是什么?其实这是Word文档的保护方法之一,具体指Word文档的编辑、修改受到了限制,需要输入密码才能进行。 设置了权限密码的Word文档还是可以直接打开,只有当需要编辑或者修改内容的时候,才会发…...

C++多态

1. 多态的概念1.1 概念多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态举个例子:比如买票这个行为,当普通人买票时,是全价买票&#xff1b…...

访问学者如何申请美国J1签证?

一、申请美国J1签证的步骤: 第一步:填写I901表。 填写I901表会收取SERVIS费用180美元,可以用VISA/Master卡直接网上支付。填完后打印收据单或者存成PDF后续再打印,记下I901收据编号。 第二步:DS-160表填写。 填写DS-…...

使用gitlab ci/cd来发布一个.net 项目

gitlab runner的安装和基本使用:https://bear-coding.blog.csdn.net/article/details/120591711安装并给项目配置完gitlab runner后再操作后面步骤。实现目标:master分支代码有变更的时候自动构建build。当开发人员在gitlab上给项目打一个tag标签分支的时候自动触发…...

笔试题-2023-蔚来-数字芯片设计【纯净题目版】

回到首页:2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 推荐内容:数字IC设计学习比较实用的资料推荐 题目背景 笔试时间:2022.08.24应聘岗位:校招-芯片逻辑综合工程师-智能硬件笔试时长:90min笔试平台:nowcoder牛客网题目类型:不定项选择题(15道)、填空题…...

ThreadLocal 详解

ThreadLocal简介JDK源码对ThreadLocal类的注释如下:ThreadLocal提供线程局部变量,使得每个线程都有自己的、独立初始化的变量副本ThreadLocal实例通常是类中的private static字段,用于将状态与线程相关联,如用户ID、事务ID只要线程…...

【Java 面试合集】重写以及重载有什么区别能简单说说嘛

重写以及重载有什么区别能简单说说嘛 前述 这是一道非常基础的面试题,我们在回答的过程中一定要逐一横向比较。 从方法的 修饰符,返回值,方法名,含义,参数等方面进行逐一分析来比较不同。 话不多话,看下…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

适应性Java用于现代 API:REST、GraphQL 和事件驱动

在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化

是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可&#xff0c…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...

xmind转换为markdown

文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...