【多态】底层原理

图片名称

博主首页: 有趣的中国人

专栏首页: 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.hqwc.cn/news/640825.html

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

      相关文章

      大模型培训老师叶梓:通过微调提升小型语言模型的复杂推理能力

      在人工智能的快速发展中&#xff0c;复杂推理能力的提升一直是研究者们追求的目标。最近&#xff0c;一项发表在arXiv上的研究成果【1】&#xff0c;提出了一种创新的方法&#xff0c;即通过微调小型语言模型&#xff08;LMs&#xff09;&#xff0c;并将其与大型语言模型&…

      LabVIEW轴承表面缺陷检测系统

      LabVIEW轴承表面缺陷检测系统 为了解决轴承生产中人工检测效率低下、误检率高的问题&#xff0c;实现了一套基于LabVIEW的轴承表面缺陷自动检测系统。该系统利用工业相机采集轴承图像&#xff0c;通过图像处理技术对轴承表面的划痕缺陷和倒角缺陷进行自动识别和分析&#xff0…

      Golang | Leetcode Golang题解之第43题字符串相乘

      题目&#xff1a; 题解&#xff1a; func multiply(num1 string, num2 string) string {if num1 "0" || num2 "0" {return "0"}m, n : len(num1), len(num2)ansArr : make([]int, m n)for i : m - 1; i > 0; i-- {x : int(num1[i]) - 0fo…

      033——项目管理

      目录 1、 gitee标签管理 2、 新建issue 3、 issue关闭流程 026——项目管理与由来-CSDN博客 1、 gitee标签管理 级别从上到下依次降低。 2、 新建issue 新建issue必须包含四项&#xff0c;名称、标签、里程碑、描述信息。 描述信息尽量完全。 名称格式为【编号】【级别】…

      Opencv_3_图像对象的创建与赋值

      ColorInvert.h 如下&#xff1a; #include <opencv.hpp> using namespace std; #include <opencv.hpp> using namespace cv; using namespace std; class ColorInvert{ public : void mat_creation(); }; ColorInvert.cpp 文件如下&#xff1a; #include &q…

      el-upload组件如何上传blob格式的url地址视频

      el-upload组件如何上传blob格式的url地址视频 一、存在问题二、直接上代码 需求&#xff1a;想把视频地址url:“blob:http://localhost:8083/65bd3c0f-52ec-4844-b85e-06fdb5095b7b”&#xff0c;通过el-upload组件上传 el-upload是Element UI中用于文件上传的组件&#xff0c;…

      ExcelVBA把当前工作表导出为PDF文档

      我们先问问Kimi Excel导出为PDF的方法有多种&#xff0c;以下是一些常见的方法&#xff1a; 1 使用Excel软件的内置功能&#xff1a; 打开Excel文件&#xff0c;点击“文件”菜单。选择“另存为”&#xff0c;在“保存类型”中选择“PDF”。设置保存路径和文件名&#xff0c;点…

      基于Tensorflow完成mnist数据集的数字手写体识别

      基于Tensorflow完成mnist数据集的数字手写体识别 关于知识背景CNNFCNN 关于数据集新的改变 关于知识背景 CNN 卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;简称CNN&#xff09;是一种具有局部连接、权值共享等特点的深层前馈神经网络&#xff08;Feed…

      【Hadoop3.3.6】数据块副本放置策略及解析EditLog和FsImage

      目录 一、摘要二、正文2.1 环境说明2.2 网络拓扑2.3 Hadoop副本放置策略介绍2.4 解析EditLog和Fsimage镜像文件三、小结一、摘要 通过解析存储于NameNode节点上的日志文件EditLog和镜像文件(元数据)Fsimage来反向验证HDFS的数据块副本存放策略,其目的是希望加深对Hadoop的数…

      VUE 项目 自动按需导入

      你是否有这样的苦恼&#xff0c;每个.vue都需要导入所需的vue各个方法 unplugin-auto-import 库 Vite、Webpack和Rollup的按需自动导入API 本章提供Vite、Webpack中使用说明 1. 安装 npm i -D unplugin-auto-import 2. config.js 配置文件内追加配置 2.1 Vite // vite.conf…

      Unity场景光照数据Light data asset

      首先描述一下遇到的问题&#xff0c;游戏运行过程中切换场景之后发现模型接收的光照不对。 Unity编辑模式下正常显示&#xff1a; 运行模式下从其他场景切入之后显示异常&#xff1a; 排查了灯光参数和环境光以及着色器都没发现异常。 根据ChatGPT的回答&#xff0c;问题可能…

      【函数式接口使用✈️✈️】配合策略模式实现文件处理的案例

      目录 &#x1f378;前言 &#x1f37b;一、功能描述 &#x1f37a;二、面向对象设计模式 &#x1f379;三、策略模式 &#x1f366;四、策略 VS 面向对象 &#x1f368;章末 &#x1f378;前言 小伙伴们大家好&#xff0c;上周初步了解了下函数式接口&#xff0c;Consume…