【多态】底层原理

news/2024/5/4 7:39:33
图片名称

博主首页: 有趣的中国人

专栏首页: C++进阶


    本篇文章主要讲解 多态底层原理 的相关内容

      1. 多态原理

      1.1 虚函数表


      先看一下这段代码,计算一下sizeof(Base)是多少:

      class Base
      {
      public:virtual void Func1(){cout << "Func1()" << endl;}
      private:int _b = 1;char _ch = 'd';
      };
      

      我这里以32位机器为例:很多人会认为这里是8字节,但是肯定没那么简单,这里答案是12字节。为什么呢?

      在这里插入图片描述

      这里不但有两个成员变量,还有一个虚表指针:__vfptr: virtual function pointer

      在这里插入图片描述

      在一个含有虚函数的类中,一定会存在一个虚表指针,因为虚函数的地址会存放在虚表当中,那么派生类中的这个表都有什么呢?接着往下分析。


      上面的代码进行改造:

      1. 增加一个派生类Derive去继承Base
      2. Derive中重写Func1
      3. Base再增加一个虚函数Func2和一个普通函数Func3

      代码如下:

      class Base
      {
      public:virtual void Func1(){cout << "Base::Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}void Func3(){cout << "Base::Func3()" << endl;}
      private:int _b = 1;
      };
      class Derive : public Base
      {
      public:virtual void Func1(){cout << "Derive::Func1()" << endl;}
      private:int _d = 2;
      };
      int main()
      {Base b;Derive d;return 0;
      }
      

      通过监视窗口观察,发现基类和派生类虚表指针的地址是不同的
      在这里插入图片描述
      在这里插入图片描述

      通过观察,我们发现,基类的虚函数表会复制到派生类的虚函数表中,如果派生类虚函数进行了重写,那么对应的虚函数地址就会改变,新函数地址会覆盖掉基类中的地址,因此重写又叫做覆盖。

      因此,多态是如何保证父类的指针调用虚函数就访问父类,派生类的指针调用虚函数就访问派生类的呢?
      在编译阶段,编译器会检查语法,看是否满足多态的两个条件:

      1. 虚函数重写
      2. 父类的指针或者引用调用虚函数

      如果满足,就会在链接阶段直接在虚表找到对应的函数地址并调用;
      如果不满足,就会在编译阶段根据类型确定函数的地址。
      以下是不构成重写的情况:

      class Person {
      public:// 这个函数没有进入虚函数表void BuyTicket() { cout << "买票-全价" << endl; }private:int _i = 1;
      };class Student : public Person {
      public:virtual void BuyTicket() { cout << "买票-半价" << endl; }int _j = 2;
      };void Func(Person* p)
      {p->BuyTicket();
      }int main()
      {Person Mike;Func(&Mike);Person p1;Func(&p1);Student Johnson;Func(&Johnson);return 0;
      }
      

      2. 打印虚表

      虚表本质上是一个函数指针数组,即是一个数组,存放的类型是函数指针类型,我们只需要知道函数指针数组的首地址就可以访问其中的所有元素了。
      我们同样知道__vfptr存放的就是首地址,取到他就好,这里可以用指针的强制转换。

      先看一下这段代码:

      class Base {
      public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
      private:int a = 1;
      };class Derive :public Base {
      public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }virtual void func4() { cout << "Derive::func4" << endl; }
      private:int b = 2;
      };
      

      这里Derive中覆盖了Base中的func1,继承了Base中的func2fun3func4进入了虚表:
      在这里插入图片描述

      typedef void(*VFPTR)();
      void PrintVFT(VFPTR* vft)
      {for (int i = 0; i < 4; ++i){printf("%p->", vft[i]);VFPTR v = vft[i];(*v)();}
      }
      int main()
      {Base b;Derive d;VFPTR* ptr = (VFPTR*)(*((int*)(&d)));PrintVFT(ptr);return 0;
      }
      

      在这里插入图片描述


      http://www.mrgr.cn/p/52245640

      相关文章

      Jenkins CI/CD 持续集成专题二 Jenkins 相关问题汇总

      一 问题一 pod [!] Unknown command: package 1.1 如果没有安装过cocoapods-packager&#xff0c;安装cocoapods-packager&#xff0c;sudo gem install cocoapods-packager 1.2 如果已经安装cocoapods-packager&#xff0c;还是出现上面的错误&#xff0c;有可能是pod的安…

      Linux查看僵尸进程

      1、查看系统是否有僵尸进程 使用Top命令查找&#xff0c;当zombie前的数量不为0时&#xff0c;即系统内存在相应数量的僵尸进程。 2、定位僵尸进程 使用命令ps -A -ostat,ppid,pid,cmd |grep -e ‘^[Zz]’定位僵尸进程以及该僵尸进程的父进程。 3、杀死僵尸进程 使用Kill -…

      丁晴无硫指套:高科技产品保护的利器

      Nitrile Sulphur-Free Finger Cots: A Weapon for Protecting High-Tech Products 随着科技的不断发展&#xff0c;微型电机、精密电子器件、仪器仪表等高科技产品的制造与应用日益普及。然而&#xff0c;这些产品的制造过程中往往需要特殊的保护措施&#xff0c;以防止静电、…

      Python与数据库连接

      新建表boss create table 创建表 Code import pymysqlcon pymysql.connect(hostlocalhost,\userroot,\password,\port3306,\dbbusiness) cursorcon.cursor() cursor.execute(create table if not exists boss(id int auto_increment primary key,name varchar(20)not null…

      [第一届 帕鲁杯 CTF挑战赛 2024] Crypto/PWN/Reverse

      被一个小题整坏了&#xff0c;后边就没认真打。赛后把没作的复盘一下。 比赛有52个应急响应&#xff0c;猜是取证&#xff0c;都是队友在干&#xff0c;我也不大关心。前边大多题是比赛的原题。这是后来听说的&#xff0c;可都没见过&#xff0c;看来打的比赛还是少了。 Cryp…

      个人网站的SEO优化系列——如何实现搜索引擎的收录

      如果你自己做了一个网站&#xff0c;并且想让更多的人知道你的网站&#xff0c;那么无非就是两种途径 一、自己进行宣传&#xff0c;或者花钱宣传 二、使用搜索引擎的自然流量 而如果搜索引擎都没有收录你的站点&#xff0c;别说是自然流量&#xff0c;就算是使用特定语句【sit…

      ASP.NET MVC企业级程序设计 (商品管理:小计,总计,删除,排序)

      目录 效果图 实现过程 1创建数据库 2创建项目文件 3创建控制器&#xff0c;右键添加&#xff0c;控制器 ​编辑 注意这里要写Home​编辑 创建成功 数据模型创建过程之前作品有具体过程​编辑 4创建DAL 5创建BLL 6创建视图&#xff0c;右键添加视图 ​编辑 7HomeCont…

      算法项目(9)—— 大模型实现PDF检索加QA

      本文包含什么&#xff1f; 使用大语言模型进行多个PDF问答检索加QA.gradio实现的网页界面操作,全套代码以及代码介绍运行有问题? csdn上后台随时售后. 项目说明 本项目实现使用大语言模型为核心,gradio框架,调用vicuna实现多个pdf QA 代码运行 python3 main.py注意事项:…

      visionTransformer window平台下报错

      错误&#xff1a; KeyError: Transformer/encoderblock_0/MlpBlock_3/Dense_0kernel is not a file in the archive解决方法&#xff1a; 修改这个函数即可&#xff0c;主要原因是Linux系统与window系统路径分隔符不一样导致 def load_from(self, weights, n_block):ROOT f&…

      day07 51单片机-串口通信

      51 单片机-串口通信 1 串口通信 1.1 需求描述 本案例讲解如何通过串口和PC以9600波特率,无校验位、1停止位通信。最终实现PC向单片机发送字符串,单片机回复PC。本案例中采用串口1通信。 1.2 硬件设计 1.2.1 串口工作原理 串口是将数据按照比特逐一发送的通信接口。在串…

      开源博客项目Blog .NET Core源码学习(17:App.Hosting项目结构分析-5)

      本文学习并分析App.Hosting项目中前台页面的作品展示页面和首页页面。 作品展示页面 作品展示页面总体上为上下布局&#xff0c;上方显示导航菜单&#xff0c;下方从左向右显示图片数据&#xff0c;支持放大查看图片和下载图片。整个页面使用了layui中的面包屑导航、弹出层、流…

      如何判别三角形和求10 个整数中最大值?

      分享每日小题&#xff0c;不断进步&#xff0c;今天的你也要加油哦&#xff01;接下来请看题------> 一、已知三条边a&#xff0c;b&#xff0c;c能否构成三角形&#xff0c;如果能构成三角形&#xff0c;判断三角形的类型&#xff08;等边三角形、等腰三角形或普通三角形 …

      读天才与算法:人脑与AI的数学思维笔记07_数字绘画

      数字绘画1. 数字绘画 1.1. 事物的可预测性与不可预测性构成了我们熟识的世界 1.1.1. 汤姆斯托帕德(Tom Stoppard) 1.2. 艺术作品就是通过各种形式给人带来美的感受,从而使人们获得精神上的愉悦与放松 1.3. “像素化”的画作激发了人…

      jmeter 指定QPS压测接口

      文章目录 jmeter 指定QPS压测接口更换语言为中文创建测试任务新建线程组右键线程组&#xff0c;新建http request&#xff0c;填写要你要压测的接口地址、参数如果需要自定义请求头&#xff0c;添加一个Http头信息管理器要查看结果和QPS统计数据&#xff0c;给上门的http请求添…

      牛客NC162 二叉树中和为某一值的路径(三)【中等 dfs C++、Java、Go、PHP】

      题目 题目链接&#xff1a; https://www.nowcoder.com/practice/965fef32cae14a17a8e86c76ffe3131f 思路 既然要找所有路径上节点和等于目标值的路径个数&#xff0c;那我们肯定先找这样的路径起点啊&#xff0c; 但是我们不知道起点究竟在哪里&#xff0c; 而且任意节点都有…

      项目大集成

      一 keeplived 高可用 192.168.11.11nginx keeplived192.168.11.12nginx keeplived 两台均编译安装服务器 1 主服务器修改文件&#xff1a; 2 备服务器修改文本 scp keepalived.conf 192.168.11.12:/etc/keepalived/ 3 给主服务器添加虚拟ip ifconfig ens33:0 192.168…

      机器学习-10-基于paddle实现神经网络

      文章目录 总结参考本门课程的目标机器学习定义第一步&#xff1a;数据准备第二步&#xff1a;定义网络第三步&#xff1a;训练网络第四步&#xff1a;测试训练好的网络 总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍基于paddle实现神经网络。 参考 MNIST 训练_副…

      Linux:进程状态

      Linux&#xff1a;进程状态 进程状态运行状态R状态 阻塞状态S状态D状态T状态t状态 挂起状态僵尸进程 & 孤儿进程X状态Z状态孤儿进程 进程状态 当一个可执行程序&#xff0c;被载入内存&#xff0c;获得自己的PCB&#xff0c;那么其就可以变成一个进程。也许你学习过一些进…

      java高校办公室行政事务管理系统设计与实现(springboot+mysql源码+文档)

      风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的闲一品交易平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于mvc的高校办公室行政…

      AJAX——Promise-链式调用

      1.Promise链式调用 概念&#xff1a;依靠then()方法会返回一个新生成的Promise对象特性&#xff0c;继续串联下一环任务&#xff0c;知道结束 细节&#xff1a;then()回调函数中的返回值&#xff0c;会影响新生成的Promise对象最终状态和结果 好处&#xff1a;通过链式调用&…