【多态】底层原理

news2024/12/23 14:00: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.coloradmin.cn/o/1616179.html

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

      相关文章

      Facebook的区块链应用深度分析

      去中心化身份验证的意义 在当今数字化社会中&#xff0c;身份验证的重要性不言而喻。对于Facebook这样的大型社交媒体平台来说&#xff0c;确保用户的身份真实性和数据的安全性是至关重要的。传统的中心化身份验证方式存在一定的安全风险和可信性问题&#xff0c;而去中心化身…

      成电少年学fpga培训就业班怎么样

      成电少年学是专注做FPGA培训的&#xff0c;以就业为导向&#xff0c;学习FPGA还是很有前途的&#xff0c;如果你是像电气、通信、自动化、物联网、集成电路这类专业&#xff0c;又不是名校高学历的&#xff0c;确实有必要可以考虑下校外培训机构。找工作多少会遇到一些问题&…

      机器学习-10-神经网络python实现-从零开始

      文章目录 总结参考本门课程的目标机器学习定义从零构建神经网络手写数据集MNIST介绍代码读取数据集MNIST神经网络实现测试手写的图片 带有反向查询的神经网络实现 总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍基于python实现神经网络。 参考 BP神经网络及pytho…

      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…

      计算机网络—— book

      文章目录 一、概述1.互联网的核心部分1&#xff0e;电路交换的主要特点2&#xff0e;分组交换的主要特点 2.计算机网络的性能1&#xff0e;速率2&#xff0e;带宽3&#xff0e;吞吐量4&#xff0e;时延5&#xff0e;利用率 3.计算机网络体系结构协议与划分层次具有五层协议的体…

      达芬奇调色:色彩理论入门

      写在前面 整理一些达芬奇调色的笔记博文内容涉及&#xff1a; 一级调色是什么&#xff0c;以及 调色素材格式 log&#xff0c;raw&#xff0c;rec709 简单认知理解不足小伙伴帮忙指正 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#…

      四、【易 AI】模型渲染与透明背景

      美恶相饰,命曰复周,物极则反,命曰环流。 ——《鹖冠子环流》 一、渲染帧率 以上两种移植方式,均可正常渲染出模型,但是画面是静止的,是因为没有调用 update 方法来刷新窗口渲染内容,我们可以通过 QTimer 来控制渲染帧率,以 MyOpenGLWindow 为例,做以下修改, #ifnde…

      GHO文件安装到Vmware的两种姿势

      1、使用 Ghost11.5.1.2269 将gho转换为vmdk文件(虚拟机硬盘)&#xff0c;Vmware新建虚拟机自定义配置&#xff0c;然后添加已有的虚拟硬盘文件。 注意ghost的版本&#xff0c;如果你是用Ghost11.5备份的gho文件&#xff0c;再用Ghost12把gho文件转换为vmdk&#xff0c;则vmdk文…

      C++及QT的线程学习

      目录 一. 线程学习 二. 学习线程当中&#xff0c;得到的未知。 1. 了解以下MainWindow和main的关系 2. []()匿名函数 有函数体&#xff0c;没有函数名. 3. join和detach都是用来管理线程的生命周期的&#xff0c;它们的区别在于线程结束和资源的回收。 4. operator()() 仿…

      Spark-机器学习(4)回归学习之逻辑回归

      在之前的文章中&#xff0c;我们来学习我们回归中的线性回归&#xff0c;了解了它的算法&#xff0c;知道了它的用法&#xff0c;并带来了简单案例。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请…

      管理集群工具之LVS

      管理集群工具之LVS 集群概念 将很多机器组织在一起&#xff0c;作为一个整体对外提供服务集群在扩展性、性能方面都可以做到很灵活集群分类 负载均衡集群&#xff1a;Load Balance高可用集群&#xff1a;High Availability高性能计算&#xff1a;High Performance Computing …

      OpenCV轻松入门(九)——使用第三方库imgaug自定义数据增强器

      安装命令&#xff1a;pip install imgaug 代码实现&#xff1a; import cv2 import random import matplotlib.pyplot as pltfrom imgaug import augmenters as iaa # 数据增强——缩放效果 def zoom_img(img):# 获取一个1-1.3倍的线性图像处理器&#xff0c;scale参数是缩放范…

      计算机视觉 | 交通信号灯状态的检测和识别

      Hi&#xff0c;大家好&#xff0c;我是半亩花海。本项目旨在使用计算机视觉技术检测交通信号灯的状态&#xff0c;主要针对红色和绿色信号灯的识别。通过分析输入图像中的像素颜色信息&#xff0c;利用OpenCV库实现对信号灯状态的检测和识别。 目录 一、项目背景 二、项目功能…

      项目大集成

      一 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…

      React.js 3D开发快速入门

      如果你对 3D 图形的可能性着迷&#xff0c;但发现从头开始创建 3D 模型的想法是不可能的 - 不用担心&#xff01; Three.js 是一个强大的 JavaScript 库&#xff0c;它可以帮助我们轻松地将现有的 3D 模型集成到 React 应用程序中。因此&#xff0c;在本文中&#xff0c;我将深…

      DHCP服务器配置故障转移后显示红色箭头、与伙伴服务器失去联系的解决方案

      一、遇到的故障现象&#xff1a; &#xff08;主DHCP服务器与备用DHCP服务器连通性正常&#xff0c;在故障转移选项卡上却显示与伙伴失去联系、伙伴关闭&#xff0c;且ipv4协议旁边显示一个红色的小箭头&#xff09;&#xff0c;正常情况下是绿色 &#xff08;一&#xff09;…

      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的安…

      通过创新的MoE架构插件缓解大型语言模型的世界知识遗忘问题

      在人工智能领域&#xff0c;大型语言模型&#xff08;LLM&#xff09;的微调是提升模型在特定任务上性能的关键步骤。然而&#xff0c;一个挑战在于&#xff0c;当引入大量微调数据时&#xff0c;模型可能会遗忘其在预训练阶段学到的世界知识&#xff0c;这被称为“世界知识遗忘…

      Valentina Studio Pro for Mac:强大的数据库管理工具

      Valentina Studio Pro for Mac是一款功能全面、操作高效的数据库管理工具&#xff0c;专为Mac用户设计&#xff0c;旨在帮助用户轻松管理各种类型的数据库。 Valentina Studio Pro for Mac v13.10激活版下载 该软件拥有直观的用户界面&#xff0c;使得数据库管理变得简单直观。…

      JS - 以工厂模式和原型模式方式建造对象、JS的垃级回收机制、数组的使用

      创建对象的方式 使用工厂方法来建造对象 在JS中我们可以通过以下方式进行创建对象&#xff1a; var obj {name:"孙悟空",age:18,gender:"男",sayName:function(){alert(this.name);}};var obj2 {name:"猪八戒",age:28,gender:"男",…