【多态】底层原理

图片名称

博主首页: 有趣的中国人

专栏首页: 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.mzph.cn/web/2026.shtml

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

      相关文章

      Oracle 21 C 安装详细操作手册,并配置客户端连接

      Oracle 21 C 安装详细操作手册 Win 11 Oracle 21C 下载&#xff1a; Database Software Downloads | Oracle 中国 云盘共享 链接&#xff1a;https://pan.baidu.com/s/12XCilnFYyLFnSVoU_ShaSA 提取码&#xff1a;nfwc Oracle 21C 配置与登陆&#xff1a; 开始菜单 NetMa…

      Node.js身份核验接口、身份证二、三要素实名认证接口

      随着互联网的高速发展&#xff0c;人们可以发表言论的渠道越来越多。网络平台不断汲取各地、各人、各时发表的各种信息。人们喜欢将信息发布到微博、知乎、天涯、豆瓣等等网络平台&#xff0c;逐步的&#xff0c;网络信息进入大爆炸时代。这些大量涌现的信息中难免掺杂着一些不…

      数学建模:Matlab一元二次回归模型-重庆房价预测

      摘要 自1998年我国实行住房改革以来,房地产行业已经逐渐成长为拉动中国经济增长的龙头产业。近几年在国家积极的财政政策刺激下,我国房地产市场处于不断发展阶段。然而,与美国等发达国家住房市场进入成熟期不同,我国正处在城市化和工业化进程加速阶段,住房水平低和需求比…

      java中spring底层核心原理解析(1)

      相关系列 java中spring底层核心原理解析(2)-CSDN博客 总起 本章主要是讲以下的内容 Bean的生命周期底层原理依赖注入底层原理初始化底层原理推断构造方法底层原理 先看spring入门代码&#xff1a; ClassPathXmlApplicationContext context new ClassPathXmlApplicationCo…

      Git下载安装

      天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

      华为认证云计算前景如何

      互联网/移动互联网经历了高速发展的二十年&#xff0c;我们有幸一起见证了华为、阿里、腾讯、百度、字节跳动、京东、滴滴、拼多多等互联网公司的崛起&#xff0c;让普通技术人实现逆袭拿到高薪&#xff0c;也让小镇做题家们有了阶层跨越的机会。 但机会都是留给有准备的人&…

      C++:特殊成员函数

      构造函数、析构函数和拷贝构造函数是C类中的三种特殊成员函数&#xff0c;它们分别用于对象的初始化、清理和拷贝操作。 1.构造函数&#xff08;Constructor&#xff09;&#xff1a;构造函数在对象创建时自动调用&#xff0c;用于初始化对象的成员变量。它的名称与类名相同&a…

      基于大语言模型的本地知识库问答系统构建方案

      文章大纲 RAG (Retrive,Augment,Generate)检索增强生成方案简介1. 数据准备阶段2. 应用阶段基于 LangChain 的实现开源实现参考RAG 与其他方案比较知识图谱方案: KG-RAG 表示基于知识图谱的RAG(Retrieval Augmented Generation)检索增强多模态检索结论参考文献与学习路径写…

      使用机器学习算法构建问答系统

      长时间以来&#xff0c;我一直在阅读关于人工智能和机器学习的多篇文章、一些论文和大量博客文章。最近&#xff0c;神经网络的进步特别引人注目&#xff0c;比如产生类似人类水平文本的 GPT3.5 模型。为了理解使用神经网络进行自然语言处理的最新技术&#xff0c;我想设计一个…

      富格林:戒备虚假套路保障安全

      富格林悉知&#xff0c;现货黄金是一种传统而又具有吸引力的投资方式&#xff0c;因为它通常被视为一种避险资产。对于投资者来说&#xff0c;特别是新进场的新手投资者戒备虚假套路是保障做单安全的关键之处。那么我们该如何进行戒备虚假套路以达到安全做单的目标呢&#xff1…

      React Router 6 + Ant Design:构建基于角色的动态路由和菜单

      要根据用户的角色生成不同的路由菜单并实现权限控制,你可以采取以下步骤: 定义路由配置 首先,你需要定义一个包含所有可能路由的配置文件,例如: const routes [{path: /dashboard,element: <DashboardPage />,roles: [admin, manager, user]},{path: /users,element:…

      Node.js 基础学习

      文章目录 1. Node.js1.1 是什么&#xff1f;1.2 作用 2. 命令行工具2.1 命令的结构2.2 常用命令 3. Node.js 注意点3.1 Node.js 中不能使用DOM 和BOM 的API3.2 Node.js 中顶级对象叫做global 4. Buffer4.1 Buffer 特点4.2 Buffer 创建方式4.3 Buffer 操作与注意点 5. 计算机基础…

      CSS基础:浮动(float)的3种方式,清除浮动3种方式的详解

      你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

      【报错】ModuleNotFoundError: No module named ‘einops‘

      1 报错 💔💔💔 ModuleNotFoundError: No module named einops 解决方法 💚 💚 💚 pip --default-timeout=100 install einops -i https://pypi.tuna.tsinghua.edu.cn/simple 问题解决啦!!!🌺🌺🌺 2 报错 💔💔💔 ModuleNotFoundError: No module

      iOS OC项目中引入SwiftUI文件

      iOS OC项目中引入SwiftUI文件 1、创建SwiftUI文件 2、第一次创建时&#xff0c;Xcode会提示桥接&#xff0c;选择 Creat Bridging Header即可。 3、创建swift管理类 /**在UIKit中使用SwiftUI&#xff0c;需要使用UIHostingController对SwiftUI进行包装&#xff0c;返回的是U…

      C++11 数据结构7 队列的链式存储,实现,测试

      前期考虑 队列是两边都有开口&#xff0c;那么在链式情况下&#xff0c;线性表的链式那一边作为对头好呢&#xff1f; 从线性表的核心的插入和删除算法来看&#xff0c;如果在线性表链表的头部插入&#xff0c;每次循环都不会走&#xff0c;但是删除的时候&#xff0c;要删除线…

      echarts 双堆叠柱状图(数据整理)

      1.后台返回的数据格式 {"code": "0000","message": "","messageCode": "操作成功","sign": null,"detail": null,"data": {"pieChart": [{"key": "产品…

      C语言--基础面试真题

      1、局部变量和静态变量的区别 普通局部变量和静态局部变量区别 存储位置&#xff1a; 普通局部变量存储在栈上 静态局部变量存储在静态存储区 生命周期&#xff1a; 当函数执行完毕时&#xff0c;普通局部变量会被销毁 静态局部变量的生命周期则是整个程序运行期间&#…

      基于51单片机的电子秤LCD1602液晶显示( proteus仿真+程序+设计报告+讲解视频)

      基于51单片机电子秤LCD显示 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真设计4. 程序代码5. 设计报告6. 设计资料内容清单&&下载链接 基于51单片机电子秤LCD显示( proteus仿真程序设计报告讲解视频&#xff09; 仿真图proteus8.9及以上 程序编译器&#xf…

      深兰科技入选2024全国“人工智能+”行动创新案例TOP100

      近日&#xff0c;中科院《互联网周刊》联合eNET研究院、德本咨询、中国社会科学院信息化研究中心共同发布了《2024全国“人工智能”行动创新案例TOP100》榜单。经评委会层层遴选&#xff0c;深兰科技专为洛阳市打造的“工业智能化洛阳中心”项目成功入围该榜单。一同入围的还包…