多态——细致讲解

🔶多态基础概念
 🔶概念
  🔱多态性
  🔱多态——重新(覆盖)
 🔶示例
  🔶基本使用方法
  🔶特例
   🔱协变
   🔱析构函数重写
 🔱多态原理
  🔱1. 虚函数形成虚表
  🔱2. 虚函数存储位置(覆盖)
  🔱3. 多态中重写的虚函数存储位置
   🔱1. 重写原理——虚表
   🔱2. 单继承中,子类新增虚函数会存到父类的虚表中——普通继承
   🔱3. 单继承中,子类新增虚函数会存到父类的虚表中——虚继承
   🔱4. 同类公用一个虚表;父类和子类不共用一张虚表
 🔱多态例题
🔱经典问题

多态基础概念

概念

多态性

 1. 静态多态:函数重载和运算符重载
 2. 动态多态:继承和虚函数

多态——重写(覆盖)

 1. 父类的指针/引用调用虚函数
 2. 调用的虚函数必须是子类重写的虚函数
这样就能在指针调用相应的对象函数的时候使用相应的成员函数,具体看示例
这里条件很严格
重写的函数要是一摸一样——返回值,函数名,参数个数,参数位置,参数类型都要完全一样,虚函数之后的const也要一样

示例

基本使用方法
  1. 父类中需要使用virtual修饰函数,子类中virtual可以不写
class A
{
public:virtual void func(){puts("A-->func");}
};
class B:public A
{
public:virtual void func(){puts("B-->func");}
};
int main()
{// 父类指向子类A* a1 = new B;a1->func();// 父类指向父类a1 = new A;a1->func();// 父类引用子类B tb;A& a2 = tb;a2.func();// 父类引用父类A ta;A& a3 = ta; // 不能直接使用a2=ta,引用不能重新赋值,虽然他不会报错,但是他的结果是错的a3.func();return 0;
}

在这里插入图片描述


  1. final 修饰类——不能继承

在这里插入图片描述

修饰虚函数——不能背重写

在这里插入图片描述

  1. override ——这个函数一定要重新父类的某一个虚函数

在这里插入图片描述

一定要注意这两个关键字加载虚函数结尾

特例
协变

虚函数的返回值可以不一样,只能出现父类返回父类的指针/引用,子类返回子类的指针/引用

在这里插入图片描述

不可以一个返回指针,一个返回引用
只能同时返回指针/同时返回引用

析构函数重写
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:virtual void fun1(){cout << "A::fun1" << endl;}virtual void fun2(){cout << "A::fun2" << endl;}~A(){cout << "delete A" << endl;}int _a = 1;};
class B :public A
{
public:virtual void fun1(){cout << "B::fun1" << endl;}virtual void fun3(){cout << "B::fun3" << endl;}~B(){cout << "delete B" << endl;}int _b = 2;
};
int main()
{A* a = new B;delete a;return 0;
}

在这里插入图片描述

delete释放看的是类型,也就是说这里delete调用的是A的析构函数
根本上说,delete会被处理成—> destructor() + operator delete,所以他们能构成重写,在具体实现的时候需要写成virtual

	virtual ~A(){cout << "delete A" << endl;}

在这里插入图片描述


多态原理

1. 虚函数形成虚表
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:virtual void fun1(){cout << "A::fun1" << endl;}virtual void fun2(){cout << "A::fun2" << endl;}};
int main()
{A a;return 0;
}

在这里插入图片描述

2. 虚函数存储位置

虚函数和普通函数放在一起,虚表存储在代码段

3. 多态中重写的虚函数存储位置
🎭1. 重写原理——虚表
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:virtual void fun1(){cout << "A::fun1" << endl;}virtual void fun2(){cout << "A::fun2" << endl;}};
class B :public A
{
public:virtual void fun1(){cout << "B::fun1" << endl;}
};
int main()
{B b;return 0;
}

在这里插入图片描述

🎭2. 单继承中,子类新增虚函数会存到父类的虚表中——普通继承
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:virtual void fun1(){cout << "A::fun1" << endl;}virtual void fun2(){cout << "A::fun2" << endl;}};
class B :public A
{
public:virtual void fun1(){cout << "B::fun1" << endl;}virtual void fun3(){cout << "B::fun3" << endl;}
};
int main()
{B b;print((T*)(*(int*)(&b)));return 0;
}

在这里插入图片描述
vs中虚表通常在最后一个都是0,Linux不是

在这里插入图片描述

🎭3. 单继承中,子类新增虚函数会存到父类的虚表中——虚继承
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:virtual void fun1(){cout << "A::fun1" << endl;}virtual void fun2(){cout << "A::fun2" << endl;}int _a = 1;};
class B :virtual public A
{
public:virtual void fun1(){cout << "B::fun1" << endl;}virtual void fun3(){cout << "B::fun3" << endl;}int _b = 2;
};
int main()
{B b;A* a = &b; // 要注意这种写法,确保他能准确跳到下一个虚表处print((T*)(*(int*)(a)));print((T*)(*(int*)(&b)));return 0;
}

在这里插入图片描述
在这里插入图片描述

🎭4. 同类公用一个虚表;父类和子类不共用一张虚表
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:virtual void fun1(){cout << "A::fun1" << endl;}virtual void fun2(){cout << "A::fun2" << endl;}int _a = 1;};
class B :virtual public A
{
public:virtual void fun1(){cout << "B::fun1" << endl;}int _b = 2;
};
int main()
{A a;B b1;B b2;return 0;
}

在这里插入图片描述


🖼多态例题

class A
{
public:virtual void fun(int val = 1){cout << "val = " << val << endl;}virtual void test(){fun();}
};
class B :public A
{
public:virtual void fun(int val = 0){cout << "val = " << val << endl;}
};
int main()
{A* a = new B;a->test();return 0;
}
void print(T a[])
{for (int i = 0; a[i] != 0; i++){printf("[%d]--->%p\n", i, a[i]);}puts("");
}
int main()
{B b;print((T*)(*(int*)(&b)));return 0;
}

在这里插入图片描述

  1. 父类指向子类,调用的test函数,test函数是父类的虚函数,类内的函数有一个默认的this指针,test内部调用的fun函数实际上是this->fun(this类型是A*——父类的指针指向虚函数),fun是子类重写的虚函数(函数是子类重写的虚函数)——满足多态条件
  2. 虚函数中的this是根据是否重写确定的,这里的test没有被重写,是A*this指针,然后调用fun,fun是经过重写的函数,所以调用的是重写的函数
  3. 虚函数继承的是函数的接口,重写的是函数的实现

所以缺省值才是1


#include<iostream>
using namespace std;class A
{
public:virtual void fun(int val = 0){printf("A::fun()--> %d", val);}virtual void test(){fun();}
};
class B:public A
{
public:void fun(int val = 1){printf("B::fun()--> %d", val);}
};
int main()
{B b;b.test();return 0;
}

在这里插入图片描述


🔒经典问题

  1. 什么是多态?
  2. 什么是重载、重写(覆盖)、重定义(隐藏)?
  3. 多态的实现原理?
  4. inline函数可以是虚函数吗?

可以,不构成多态就是inline,构成多态就不是inline

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

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

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

不能,虚表在编译时生成
在调用构造函数之后,但是虚表指针在成员初始化之前

  1. 析构函数可以是虚函数吗?

本就应该是,在A* a = new B;这种场景下,在释放子类对象时,需要将析构函数变成虚函数

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

首先如果是普通对象,是一样快的。如果构成多态,就是普通函数快,因为运行时调用虚函数需要到虚函数表中去查找。

  1. 虚函数表是在什么阶段生成的,存在哪的?

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

  1. C++菱形继承的问题?虚继承的原理?

菱形继承会造成祖宗类数据冗余的问题
在每一个继承自祖宗类的派生类中,使用一个指针指向一个偏移量,根据偏移量找到的地址就是祖宗类的数据,并且这个数据只有一份

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

抽象类含有形如 virtual void fun() =0; 的基类/派生类
强制派生类重写父类的实现


原理的角度理解,重写之后将fun虚函数进行覆盖test是A*this调用经过重写的虚函数fun符合多态的条件,并且继承的是接口不是实现fun虚函数继承父类函数接口,并使用重写的虚函数实现,最终形成了这个样子


优秀多态文章
优秀多态文章
为什么要使用父类指针和引用实现多态,而不能使用对象?
虚析构函数
虚表位置
虚表位置

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

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

相关文章

redis实战笔记汇总

文章目录 1 NoSQL入门概述1.1 能干嘛&#xff1f;1.2 传统RDBMS VS NOSQL1.3 NoSQL数据库的四大分类1.4 分布式数据库CAP原理 BASE原则1.5 分布式集群简介1.6 淘宝商品信息的存储方案 2 Redis入门概述2.1 是什么&#xff1f;2.2 能干嘛&#xff1f;2.3 怎么玩&#xff1f;核心…

46、WEB攻防——通用漏洞PHP反序列化原生类漏洞绕过公私有属性

文章目录 几种常用的魔术方法1、__destruct()2、__tostring()3、__call()4、__get()5、__set()6、__sleep()7、__wakeup()8、__isset()9、__unset()9、__invoke() 三种变量属性极客2019 PHPphp原生类 几种常用的魔术方法 1、__destruct() 当删除一个对象或对象操作终止时被调…

像用Excel一样用Python:pandasGUI

文章目录 启动数据导入绘图 启动 众所周知&#xff0c;pandas是Python中著名的数据挖掘模块&#xff0c;以处理表格数据著称&#xff0c;并且具备一定的可视化能力。而pandasGUI则为pandas打造了一个友好的交互窗口&#xff0c;有了这个&#xff0c;就可以像使用Excel一样使用…

【Spring Boot 3】的安全防线:整合 【Spring Security 6】

简介 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多&#xff0c;因为相比与Sp…

Linux线程【互斥与同步】

目录 1.资源共享问题 1.1多线程并发访问 1.2临界区和临界资源 1.3互斥锁 2.多线程抢票 2.1并发抢票 2.2 引发问题 3.线程互斥 3.1互斥锁相关操作 3.1.1互斥锁创建与销毁 3.1.2、加锁操作 3.1.3 解锁操作 3.2.解决抢票问题 3.2.1互斥锁细节 3.3互斥…

github用法详解

本文是一篇面向全体小白的文章,图文兼备。为了让小白们知道如何使用GitHub,我努力将本文写得通俗易懂,尽量让刚刚上网的小白也能明白。所以各位程序员们都可以滑走了~ 啥是GitHub? 百度百科会告诉你, GitHub是一个面向开源及私有软件项目的托管平台,因为只支持Git作为…

Topaz Gigapixel AI:让每一张照片都焕发新生mac/win版

Topaz Gigapixel AI 是一款革命性的图像增强软件&#xff0c;它利用先进的人工智能技术&#xff0c;能够显著提升图像的分辨率和质量。无论是摄影爱好者还是专业摄影师&#xff0c;这款软件都能帮助他们将模糊的、低分辨率的照片转化为清晰、细腻的高分辨率图像。 Topaz Gigap…

JavaWeb——011 SpringBootWeb综合案例(删除/修改员工、文件上传、配置文件)

SpringBootWeb案例 目录 SpringBootWeb案例1. 新增员工1.1 需求1.2 接口文档1.3 思路分析1.4 功能开发1.5 功能测试1.6 前后端联调 2. 文件上传2.1 简介2.2 本地存储2.3 阿里云OSS2.3.1 准备2.3.2 入门2.3.3 集成 3. 修改员工3.1 查询回显3.1.1 接口文档3.1.2 实现思路3.1.3 代…

07 编译器

目录 编译过程编译器查看详解函数库自动化构建工具进度条程序 1. 编译过程 预处理: a. 去注释 b.宏替换 c.头文件展开 d.条件编译 编译: 汇编 汇编: 可重定向二进制目标文件 链接: 链接多个.o, .obj合并形成一个可执行exe gcc编译c程序, g编译c程序 2. 编译器查看 输入gcc …

mac苹果电脑c盘满了如何清理内存?2024最新操作教程分享

苹果电脑用户经常会遇到麻烦:内置存储器(即C盘)空间不断缩小&#xff0c;电脑运行缓慢。在这种情况下&#xff0c;苹果电脑c盘满了怎么清理&#xff1f;如何有效清理和优化存储空间&#xff0c;提高计算机性能&#xff1f;成了一个重要的问题。今天&#xff0c;我想给大家详细介…

备战蓝桥杯---线段树基础2

今天我们把线段树的另一个模板看一下&#xff1a; 在这里&#xff0c;我们注意到乘的操作&#xff0c;因此我们用两个懒标记来分别表示加和乘&#xff0c;这时我们面临了一个问题&#xff0c;就是当我们把标记往下传时&#xff0c;它的儿子怎么知道是先乘还是先加&#xff1f; …

2025张宇考研数学,百度网盘视频课+36讲PDF讲义+真题

张宇老师的课属于幽默生动&#xff0c;会让一个文科生爱上数学&#xff0c;但是有的同学不知道在哪看&#xff0c;可以看一下&#xff1a;2025张宇考研数学全程网盘 docs.qq.com/doc/DTmtOa0Fzc0V3WElI 可以粘贴在浏览器 张宇30讲作为一本基础讲义&#xff1a;和教材…

java的线程池介绍

什么是线程池&#xff1f; 线程池是一种用于管理和复用线程的机制&#xff0c;旨在减少线程的创建和销毁次数&#xff0c;提高线程的可重用性和执行效率。通过线程池&#xff0c;可以控制线程的数量、数量大小以及线程的执行方式&#xff0c;从而更加有效地处理并发任务。 线…

如何使用公网地址远程访问内网Nacos UI界面查看注册服务

文章目录 1. Docker 运行Nacos2. 本地访问Nacos3. Linux安装Cpolar4. 配置Nacos UI界面公网地址5. 远程访问 Nacos UI界面6. 固定Nacos UI界面公网地址7. 固定地址访问Plik Nacos是阿里开放的一款中间件,也是一款服务注册中心&#xff0c;它主要提供三种功能&#xff1a;持久化…

6、wuzhicms代码审计

wuzhicms代码审计 前言 安装环境配置 服务器要求 Web服务器: apache/nginx/iis PHP环境要求:支持php5.2、php5.3、php5.4、php5.5、php5.6、php7.1 (推荐使用5.4或更高版本!) 数据库要求: Mysql5www/install文件夹即可进入安装页面 审计开始 首页文件index.php&#xff0c…

kubernetes+prometheus+grafana监控+alertmanager实现qq邮箱报警

prometheus基于kubernetes监控 prometheus对kubernetes的监控 对于Kubernetes而言&#xff0c;我们可以把当中所有的资源分为几类&#xff1a; 基础设施层&#xff08;Node&#xff09;&#xff1a;集群节点&#xff0c;为整个集群和应用提供运行时资源容器基础设施&#xff…

C/C++内存管理及内存泄漏详解

目录 C/C内存分布 C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free C内存管理方式 new/delete操作内置类型 new和delete操作自定义类型 operator new与operator delete函数 new和delete的实现原理 内置类型 自定义类型 内存泄漏 概念 内存泄漏分类 ⭐…

180基于matlab的频率切片小波变换程序(FTWT)

基于matlab的频率切片小波变换程序&#xff08;FTWT&#xff09;。从一种新的角度出发&#xff0c;通过自由选择频率切片函数、引进新尺度参数&#xff0c;在频率域实现小波变换&#xff0c;该变换能够很好地刻画信号各成分之间的相对能量关系。此外&#xff0c;频率切片小波变…

【InternLM 实战营笔记】OpenCompass大模型评测

随着人工智能技术的快速发展&#xff0c; 大规模预训练自然语言模型成为了研究热点和关注焦点。OpenAI于2018年提出了第一代GPT模型&#xff0c;开辟了自然语言模型生成式预训练的路线。沿着这条路线&#xff0c;随后又陆续发布了GPT-2和GPT-3模型。与此同时&#xff0c;谷歌也…

探讨苹果 Vision Pro 的 AI 数字人形象问题

Personas 的设计模糊性&#xff1a; 部分人认为这种模糊设计可能是出于安全考虑&#x1f6e1;️。安全角度&#xff1a;Personas 代表着你的 AI 数字形象&#xff0c;在创建时&#xff0c;它相当于你的 AVP&#xff08;生物识别扫描器的存在增加了冒充的难度&#xff09;。如果…