【C++】三大特性之多态

1 定义及实现

1.1 概念

多态是C++三大特性之一。通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如学生买票半价这个行为,Student继承了Person。Person对象买票全价,Student对象买票半价。对于买票这个行为,产生了不同的结果。

virtual关键字只在声明时加上,在类外实现时不能加

静态成员函数与具体对象无关,属于整个类,核心关键是没有隐藏的this指针,此时没有this无法拿到虚表,就无法实现多态,因此不能设置为虚函数

1.2 多态的构成条件

由上我们可以知道,多态是基于继承关系的。而想要在继承中要构成多态还有两个条件:

  1. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

父子类中两个虚函数,三同(函数名、参数、返回值)
重写和隐藏比较类似,隐藏是函数名相同就构成隐藏,而重写的条件更加苛刻,可以看做是隐藏的子集再子集

  1. 必须通过父类的指针或引用去调用虚函数

注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用

普通调用 看指针、引用、对象的类型
多态调用 看指针、引用指向的对象

什么是虚函数呢?其实,被virtual修饰的类成员函数称为虚函数

class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

1.3 虚函数重写的两个例外

  1. 协变(基类与派生类虚函数返回值类型不同)
    虚函数返回值可以不同,但是必须是父子类关系的指针或引用,即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用
    在这里插入图片描述

编译器进行了强制检查,否则应该构成隐藏

  1. 析构函数的重写,名字特殊处理(基类与派生类析构函数的名字不同)
    如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

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

在这里插入图片描述

☆☆☆重写是实现重写

对虚函数进行重写时,用的是父类中的声明,子类中只是对函数体内容的实现进行重写,最终的函数其实是父类声明和子类实现的组合体。


2 C++11 override 和 final

  1. final:修饰虚函数,表示该虚函数不能被重写 #final
// 以下编译不通过
class Car
{
public:
	virtual void Drive() final {}
};
class Benz :public Car
{
public:
	virtual void Drive() {cout << "Benz-舒适" << endl;}
};
  1. override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
    在这里插入图片描述

3 抽象类

3.1 概念

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承

3.2 接口继承与实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。


4 多态的原理

4.1 虚函数表

实现多态的原理是虚函数表。
一个类存在虚函数,那么编译器就会为这个类生成一个虚表,在虚表里存放的是这个类所有虚函数的地址。当生成类对象的时候,编译器会自动的将类对象的前四个字节设置为虚表的地址,而这四个字节就可以看作是一个指向虚表的指针。虚表里依次存放的是虚函数的地址,每个虚函数的地址占4个字节。
![[Pasted image 20240310190553.png]]
虚函数表(本质是函数指针数组)

_vfptr 虚函数表指针,简称虚表指针

虚函数的重写也叫覆盖
重写是语法层的概念
覆盖是原理层的概念

![[Pasted image 20240310191318.png]]

解释:子类先把父类的虚表拷贝一份,如果发现有重写,就在子类虚表中将被重写的函数指针覆盖掉

![[Pasted image 20240310192430.png]]

实际就是编译器也不知道指向的是谁,只需要到指向的对象中去找虚函数表调用,本质从汇编的角度看到的就是父类,只是看该父类是原生父类还是子类切片出的父类

多态调用的函数和普通调用的函数其实是一个函数,只是调用的方式不一样
![[Pasted image 20240310193139.png]]![[Pasted image 20240312204820.png]]

子类新增虚函数,监视窗口看不到,但是内存窗口可以看到
![[Pasted image 20240312205400.png]]

虚函数表存在哪里呢?

虚表存放在常量区
一个类存在虚函数,那么编译器就会为这个类生成一个虚表,在虚表里存放的是这个类所有虚函数的地址。当生成类对象的时候,编译器会自动的将类对象的前四个字节设置为虚表的地址,而这四个字节就可以看作是一个指向虚表的指针。虚表里依次存放的是虚函数的地址,每个虚函数的地址占4个字节。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

苍穹外卖-day06:HttpClient、微信小程序开发、微信登录(业务流程)、导入商品浏览功能代码(业务逻辑)

苍穹外卖-day06 课程内容 HttpClient微信小程序开发微信登录导入商品浏览功能代码 功能实现&#xff1a;微信登录、商品浏览 微信登录效果图&#xff1a; 商品浏览效果图&#xff1a; 1. HttpClient 1.1 介绍 HttpClient 是Apache Jakarta Common 下的子项目&#xff0c;…

VPTTA:为每张医疗图像生成特定的“提示”,解决跨不同设备和条件的医疗图像分割的准确性和适应性

VPTTA&#xff1a;为每张医疗图像生成特定的“提示”&#xff0c;解决跨不同设备和条件的医疗图像分割的准确性和适应性 提出背景VPTTA 方法VPTTA 步骤 提出背景 论文&#xff1a;https://arxiv.org/pdf/2311.18363.pdf 代码&#xff1a;https://github.com/Chen-Ziyang/VPTT…

PHP<=7.4.21 Development Server源码泄露漏洞 例题

打开题目 dirsearch扫描发现存在shell.php 非预期解 访问shell.php&#xff0c;往下翻直接就看到了flag.. 正常解法 访问shell.php 看见php的版本是7.3.33 我们知道 PHP<7.4.21时通过php -S开起的WEB服务器存在源码泄露漏洞&#xff0c;可以将PHP文件作为静态文件直接输…

【JAVA】Servlet开发

目录 HttpServlet HttpServletRequest HttpServletResponse 错误页面 设置网页自动刷新时间 构造重定向相应 js发起http请求 服务器端对js发起的http请求进行处理 前端获取后端数据&#xff0c;添加到当前页面的末尾&#xff0c;代码示例&#xff1a; 前后端交互&…

HTML选择文件的实时预览

HTML选择文件的实时预览 目录 HTML选择文件的实时预览HTML代码JS代码预览 HTML代码 <input type"file" id"adv_img_input" style"width: 1000px ;height:30px"> <img src"#"id"adv_img">JS代码 <script>…

Netty中的核心概念

事件传播机制 当pipeline中有多个handler时&#xff0c;netty内部的事件是如何向后传递到每个handler中的&#xff1f; 结论&#xff1a;需要在当前handler中手动将当前事件传递下去 1&#xff0c;如果handler是直接实现的接口&#xff0c;使用ChannelHandlerContext的fireXXX…

使用 ONLYOFFICE API 构建 Java 转换器,在 Word 和 PDF 之间进行转换

文章作者&#xff1a;ajun 随着文档处理需求的增加&#xff0c;格式转换成为了一个重要的需求点。由于PDF格式具有跨平台、不易被篡改的特性&#xff0c;将Word格式(.docx)转换为PDF格式(.pdf)的需求尤为强烈。ONLYOFFICE作为一个强大的办公套件&#xff0c;提供了这样的转换功…

修改/etc/resolve.conf重启NetworkManager之后自动还原

我ping 百度报错&#xff1a; [rootk8snode1 ~]# ping baidu.com ping: baidu.com: Name or service not known很明显&#xff0c;这是DNS解析问题。 于是我修改 /etc/resolv.conf 文件后&#xff0c;执行完sudo systemctl restart NetworkManager&#xff0c;/etc/resolv.con…

leetcode刷题(javaScript)——动态规划相关场景题总结

动态规划在 JavaScript 刷题中有一定的难度&#xff0c;但也是非常常见和重要的算法思想。动态规划通常适用于需要求解最优解、最大值、最小值等问题的场景&#xff0c;可以将复杂问题拆分成子问题&#xff0c;通过存储子问题的解来避免重复计算&#xff0c;从而提高效率。 理解…

微信小程序 nodejs+vue+uninapp学生在线选课作业管理系统

基于微信小程序的班级作业管理助手使用的是MySQL数据库&#xff0c;nodejs语言和IDEA以及微信开发者工具作为开发工具&#xff0c;这些技术和工具我在日常的作业中都经常的使用&#xff0c;并且因为对编程感兴趣&#xff0c;在闲暇时间也进行的进行编程的提高&#xff0c;所以在…

js判断对象是否有某个属性

前端判断后端接口是否返回某个字段的时候 <script>var obj { name: "John", age: 30 };console.log(obj.hasOwnProperty("name")); // 输出 trueconsole.log(obj.hasOwnProperty("email")); // 输出 falselet obj11 { name: "Joh…

Android SystemServer进程解析

SystemServer进程在android系统中占了举足轻重的地位&#xff0c;系统的所有服务和SystemUI都是由它启动。 一、SystemServer进程主函数流程 1、主函数三部曲 //frameworks/base/services/java/com/android/server/SystemServer.java /** * The main entry point from zy…

详解Python中的缩进和选择

缩进 Python最具特色的是用缩进来标明成块的代码。我下面以if选择结构来举例。if后面跟随条件&#xff0c;如果条件成立&#xff0c;则执行归属于if的一个代码块。 先看C语言的表达方式&#xff08;注意&#xff0c;这是C&#xff0c;不是Python!&#xff09; if ( i > 0 …

搜索二叉树迭代和递归的两种*简单*实现方式

判断搜索二叉树 概念 一棵树所有结点的左节点小于父节点&#xff0c;右节点大于父节点&#xff0c;则为搜索二叉树。 迭代方法 中序遍历二叉树&#xff0c;如果总是升序则是搜索二叉树。如果存在降序&#xff0c;那肯定不是搜索二叉树。 Coding checkTreeOrder()方法 boo…

Spring Bean的生命周期流程

前言 Java 中的公共类称之为Java Bean&#xff0c;而 Spring 中的 Bean 指的是将对象的生命周期&#xff0c;交给Spring IoC 容器来管理的对象。所以 Spring 中的 Bean 对象在使用时&#xff0c;无需通过 new 来创建对象&#xff0c;只需要通过 DI&#xff08;依赖注入&#x…

ElasticSearch架构设计

一、基础概念 Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene™ 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 Lucene 那么简单&#xff0c;它不仅包括了全文搜索功能&#xff0c;还可以进行以下工作: 一个分布式的实时文档…

中国移动端第三方输入法市场专题2024

易观分析&#xff1a;伴随移动互联网市场发展成熟&#xff0c;第三方输入法用户规模呈现同比小幅增长态势&#xff0c;截止2023年12月已经达到7.83亿&#xff0c;相比2022年同期增长2.7%。从运营端数据表现看&#xff0c;输入法用户粘性稳定&#xff0c;人均单日使用时长在年初…

掘根宝典之C++迭代器简介

简介 迭代器是一种用于遍历容器元素的对象。它提供了一种统一的访问方式&#xff0c;使程序员可以对容器中的元素进行逐个访问和操作&#xff0c;而不需要了解容器的内部实现细节。 C标准库里每个容器都定义了迭代器 迭代器的作用类似于指针&#xff0c;可以指向容器中的某个…

C/C++中{}的用法总结(全)

C基础专栏&#xff1a;http://t.csdnimg.cn/UjhPR 目录 1.定义初始化列表&#xff08;Initializer List&#xff09; 2.类成员初始化列表 3.无默认构造函数的类的默认初始化&#xff08;C11 及以后版本&#xff09; 4.初始化器列表构造函数&#xff08;C11 及以后版本&…

后端工程师快速使用vue和Element

文章目录 Vue1 Vue概述2 快速入门3 Vue指令3.1 v-bind和v-model3.2 v-on3.3 v-if和v-show3.4 v-for3.5 案例 4 生命周期 Element快速使用1 Element介绍2 快速入门3 当前页面中嵌套另一个页面案例代码案例截图 Vue 1 Vue概述 通过我们学习的htmlcssjs已经能够开发美观的页面了…
最新文章