当前位置: 首页 > news >正文

【C++】虚表和虚基表到底有哪些区别?

虚表和虚基表

  • 虚表
  • 虚基表
  • 虚拟继承和虚函数都存在时的对象模型

虚表

我们知道,如果类中声明了的方法是用virtual进行修饰的,则说明当前这个方法要作为虚函数,而虚函数的存储和普通函数的存储是有区别的
当有虚函数声明时,编译器会创建一个虚函数表,将当前的虚函数按照声明次序放入虚函数表中,而这个虚函数表实际上就是一个函数指针数组,然后将当前这个虚函数表的地址放入对象模型的最起始位置。

class A
{
public:virtual void fun1(){cout << "A::fun1()" << endl;}virtual void fun2(){cout << "A::fun2()" << endl;}virtual void fun3(){cout << "A::fun3()" << endl;}int _a;
};

它对应的对象模型是这样的:
在这里插入图片描述

所以说,本质上虚函数表是一个函数指针数组,而对象模型中存放的是虚函数表的首地址,当我们需要调用虚函数时,传递对应的对象,就可以通过对象的地址获取对象的虚表指针,从而获取虚表,进而得到对应虚函数表中某个虚函数的地址,以此来进行调用(知道函数的入口地址,就可以调用对应的函数)

虚基表

我们知道,当出现菱形继承时,一定会出现对象模型中有多个基类对象成员。

//普通继承
class A
{
public:int _a;
};
class B : public A
{
public:int _b;
};
class C : public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};

上述代码中,D类对象中一定会存在B类和C类对象继承自A类对象的_a这个成员,这样就出现了两份_a成员,导致访问_a时出现二义性,并且随着继承深度和广度的增加,对象成员会越来越冗余。
为了解决这个问题,出现了虚拟继承。

//菱形虚拟继承
class A
{
public:int _a;
};
class B : virtual public A
{
public:int _b;
};
class C : virtual public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};

通过让B类和C类虚拟继承A类后,对象模型就从图1变成了图2
在这里插入图片描述

在这里插入图片描述

这样的转变,使得B类和C类虽然继承了A类,但是B类和C类中并没有存储A类的对象(基类对象只有一份,被存放在了整个对象模型的最后),除了子类新增之外,只有一个指针,这个指针就被称为虚基表指针。

虚基表指针所指向的是一个虚基表,对于B类ptr1这个虚基表,总大小为8个字节(32位系统下),前4个字节存储的是子类对象相对于自己起始位置的偏移量,(目前来看是0,当存在虚函数的虚拟继承时,就不是0了),后4个字节存储是子类对象相对于基类部分的偏移量。
在这里插入图片描述
ptr2指向C类这个对象的虚基表,总大小为8个字节(32位系统下),前4个字节存储的是子类对象相对于自己起始位置的偏移量,(目前来看是0,当存在虚函数的虚拟继承时,就不是0了),后4个字节存储是子类对象相对于基类部分的偏移量。
在这里插入图片描述

可以发现,虚表在整个类对象中只存储一份,也就是说一个类的不同对象共享同一份虚表。而虚基表有多份,取决于当前类是否虚拟继承了基类,若虚拟继承了基类,就会创建一个虚基表指针,指向一个虚基表。

虚拟继承和虚函数都存在时的对象模型

那么就存在另外一个问题,当虚拟继承和虚函数同时出现在继承体系中,对象模型又是什么样子呢?

class A
{
public:virtual void fun1(){cout << "A::fun1()" << endl;}virtual void fun2(){cout << "A::fun2()" << endl;}int _a;
}
class B : virtual public A
{
public:virtual void fun1(){cout << "B::fun1()" << endl;}virtual void fun3(){cout << "B::fun3()" << endl;}int _b;
};
class C : virtual public A
{
public:virtual void fun2(){cout << "C::fun2()" << endl;}virtual void fun4(){cout << "C::fun4()" << endl;}int _c;
};
class D : public B, public C
{
public:virtual void fun1(){cout << "D::fun1()" << endl;}virtual void fun2(){cout << "D::fun2()" << endl;}virtual void fun5(){cout << "D::fun5()" << endl;}int _d;
}

上述代码中,B类和C类都继承自A类,并且对A类中的虚函数进行了重写,同时也新增了虚函数。D类继承了B和C类,对B和C类中的虚函数进行了重写,同时也新增了虚函数。

那么当前在这个继承体系下,对象模型是什么样子呢?
其实不难想到,由于B类和C类都是虚拟继承,那么A类成员只会保留一份在最下方,同时B类和C类都会保存自己的虚基表指针,而D类由于是普通继承,按照顺序,新增的虚函数被放到B类的虚表中。
在这里插入图片描述

我们通过取地址发现,对象模型确实是上述的样子,但是在D类和A类之间,放了00000000作为对象分割区分(猜测)
在这里插入图片描述

请注意:当前的验证情况是在vs2019中进行验证的。

总结:当虚基表和虚表同时存在(虚拟继承和虚函数同时存在时),对象模型从整体上来说还是和虚拟继承相同(基类对象顺序按照声明的顺序从上到下排列,对象中没有祖父类的成员,祖父类成员被放到了模型的最下方)。但是由于有虚函数的存在,B类对A类的虚函数进行重写的虚函数在A类中直接修改,B类新增的虚函数被放到B类内部的虚表中,C类对A类的虚函数进行重写的虚函数在A类中直接修改,C类新增的虚函数被放到C类内部的虚表中。D类对B类和C类进行重写的虚函数直接在对应类中进行修改,D类新增的就直接放到B类的虚表中。

通过上述的描述,可以知道对于B类,C类和A类的虚表中存放的虚函数分别为:

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

而对于虚基表来说,表示的是子类对象相对于自己起始位置的偏移量,如果是B类,B类对象的起始位置已经有了一个虚表指针,那么虚基表中前四个字节要表示相对自己起始位置的偏移量就需要为-4,而后四个字节是相对于基类的偏移量是正常的计算方式。

对于B类C类的虚基表来说,其中的值为:
在这里插入图片描述
在这里插入图片描述

相关文章:

【C++】虚表和虚基表到底有哪些区别?

虚表和虚基表 虚表虚基表虚拟继承和虚函数都存在时的对象模型 虚表 我们知道&#xff0c;如果类中声明了的方法是用virtual进行修饰的&#xff0c;则说明当前这个方法要作为虚函数&#xff0c;而虚函数的存储和普通函数的存储是有区别的 当有虚函数声明时&#xff0c;编译器会…...

剑指 Offer 04. 二维数组中的查找解题思路

文章目录 标题解题思路优化 标题 在一个 n * m 的二维数组中&#xff0c;每一行都按照从左到右 非递减 的顺序排序&#xff0c;每一列都按照从上到下 非递减 的顺序排序。请完成一个高效的函数&#xff0c;输入这样的一个二维数组和一个整数&#xff0c;判断数组中是否含有该整…...

冯诺依曼体系结构详解

一.冯诺伊曼体系结构的概念&#xff1a; 约翰冯诺依曼&#xff08;John von Neumann&#xff0c;1903.1.28-1957.2.8&#xff09;&#xff0c;美籍匈牙利数学家&#xff0c;计算机科学家&#xff0c;物理学家。是20世纪最重要的数学家之一&#xff0c;后来被称为计算机之父。 后…...

ISO证书“带标”与“不带标”的区别是什么?

ISO9001质量管理体系认证是企业产品获得“通行绿卡”的最直接最有效的途径。 通过认证在打破贸易壁垒&#xff0c;提高产品知名度&#xff0c;降低生产成本&#xff0c;提高经济效益&#xff0c;维护消费者权益&#xff0c;减少重复审核负担等方面的作用越来越为企业界所共知。…...

RocketMQ 领域模型概述

本文为您介绍 Apache RocketMQ 的领域模型。 Apache RocketMQ 是一款典型的分布式架构下的中间件产品&#xff0c;使用异步通信方式和发布订阅的消息传输模型。通信方式和传输模型的具体说明&#xff0c;请参见下文通信方式介绍和消息传输模型介绍。 Apache RocketMQ 产品具备…...

黄河千年清一回与人类健康

黄河千年清一回奏响一曲曲让人类走进幸福新时代的壮丽凯歌。疫情之后的首届全世界健康产业发展大会 5 月28 日上午 9 时在中国首都北京召开 The Yellow River has played a magnificent song of triumph in the millennium, ushering humanity into a new era of happiness. T…...

Android java层hook------xposed框架的使用

xposed曾经是android平台上最好的java层hook和调试工具&#xff0c;由于已经不再更新&#xff0c;当前支持的android系统版本比较老旧&#xff0c;目前只能支持到android6.0&#xff0c;故已经逐渐落伍&#xff0c;目前android上最广泛使用的hook工具是frida&#xff0c;这是另…...

css奇淫巧计

1.input文本内容替换 -webkit-text-security&#xff1a;通过用形状替换字符,仅影响那些字段不是的typepassword 可选值:none &#xff08;无&#xff09;&#xff0c;circle &#xff08;圆圈&#xff09;&#xff0c;disc &#xff08;圆形&#xff09;&#xff0c;square &a…...

Web服务器实现|基于阻塞队列线程池的Http服务器|线程控制|Http协议

基于阻塞队列生产者消费者模型线程池的多线程Web服务器 代码地址&#xff1a;WebServer_GitHub_Addr README 摘要 本实验通过C语言&#xff0c;实现了一个基于阻塞队列线程池的多线程Web服务器。该服务器支持通过http协议发送报文&#xff0c;跨主机抓取服务器上特定资源。与…...

【C++】运算符重载(日期类的实现)

【C】运算符重载&#xff08;日期类的实现&#xff09; 前言运算符重载operator全局和类中 日期类的实现成员变量的确定构造函数拷贝构造 运算符重载部分的重载思路实现GETmonthdayoperator 的重载思路实现 -的与-的重载实现 各个比较运算符的重载实现 前置与后置实现 &#xf…...

【Linux】线程分离 | 线程库 | C++调用线程 | 线程局部存储

文章目录 1. 线程分离1. 为什么要线程分离&#xff1f;2. 具体使用3. 为什么有时候分离在调用join 会正常运行&#xff1f; 2. 如何理解线程库&#xff1f;如何理解 先描述 在组织&#xff1f; 3. C中使用多线程4. 线程局部存储局部变量全局变量 1. 线程分离 1. 为什么要线程分…...

c++ ffmpeg 浅谈YUV444、YUV422、YUV420(2)

本期将会给大家介绍YUV相关基础知识&#xff0c;同时也介绍威创网络分布式系统的卓越色彩处理技术。 1.什么是YUV色彩空间 2.YUV采样格式 3.YUV不同采样格式对图像画质的影响分析 一、什么是YUV色彩空间? YUV是视频、图片、相机等应用中常常使用的一类图像格式&#xff0c;是…...

Redis在Windows下安装配置教程

Redis是一个开源的高性能键值对存储数据库&#xff0c;常用于缓存、消息队列等场景。本文将介绍如何在Windows系统下安装配置Redis。 1. 下载地址 Redis官方网站提供了Windows版本的安装包下载地址&#xff0c;网址为&#xff1a;https://github.com/tporadowski/redis/relea…...

数据库服务器

数据库服务器&#xff0c;联系Web服务器与DBMS的中间件是负责处理所有的应用程序服务器&#xff0c;包括在web服务器和后台的应用程序或数据库之间的事务处理和数据访问。 基本信息 中文名 数据库服务器 外文名 database server 功能 数据库服务器建立在数据库系统基础上&a…...

VS输出路径和生成事件

在生成时&#xff0c;常常希望输出文件夹整洁&#xff0c;因此需要设置dll或exe输出位置&#xff0c;同时也希望对一些文件做一些特殊操作 VS的 UI 常用缩写 “./”&#xff1a;代表目前所在的目录。 " . ./"代表上一层目录。 “/”&#xff1a;代表根目录。 生成…...

从 WebKit 看浏览器内核架构

浏览器常见的浏览器内核有&#xff1a;Blink、WebKit、Gecko、Trident 等&#xff0c;目前 WebKit 内核占据了非常大的的市场&#xff0c;包括 Chrome、Safari、安卓浏览器等市面上的主流浏览器&#xff0c;都使用了 WebKit 内核。 从 WebKit 看浏览器内核架构 既然 WebKit 这么…...

使用原生的 JavaScript,不依赖于任何特定的库与 ROSBridge进行通信

使用原生的 JavaScript&#xff0c;不依赖于任何特定的库与 ROSBridge进行通信 创建与 ROS 的连接&#xff1a; var rosbridge_url "ws://localhost:9090"; var ws new WebSocket(rosbridge_url);ws.onopen function(event) {console.log(Connected to rosbri…...

MATLAB第十章_图像处理算法

目录 图像处理算法 图像处理基础 图像处理函数 默认显示方式 添加颜色条 显示多帧图像 显示动画 三维材质图像 图像的直方图 灰度变换 均衡直方图 图像处理应用 图像增强 图像重建 图像变换 图像压缩 图像分割 图像边缘检测 图像识别 图像处理算法 图像处理…...

RobotFramework接口测试方案

1. Robot FrameWork介绍 1.1 介绍 Robot Framework是用于验收测试和回归测试的通用测试自动化框架。它使用易于理解的表格数据语法&#xff0c;非常友好的实现了关键字驱动和数据驱动模式。它的测试功能可以通过使用Python或Java实现的测试库进行扩展&#xff0c;用户可以使用…...

chatgpt赋能python:Python中日期转换:从字符串到日期对象

Python中日期转换&#xff1a;从字符串到日期对象 作为一个经验丰富的Python工程师&#xff0c;日期转换在我的日常编码工作中经常遇到。Python提供了一些内置函数和模块&#xff0c;可以将字符串转换为日期对象或将日期对象格式化为特定的字符串。本篇文章将带您深入了解Pyth…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知&#xff0c;帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量&#xff0c;能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度&#xff0c;还为机器人、医疗设备和制造业的智…...

Linux中《基础IO》详细介绍

目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改&#xff0c;实现简单cat命令 输出信息到显示器&#xff0c;你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...