C++虚继承下的类大小

2023-05-16

前言

带有虚函数的虚继承的对象模型比较复杂,所以单独整理一下。其实关于计算类大小是C++的一大易错点之一。即便是我在这儿理了半天,也不一定就是正确的,如果大佬看到本文,发现我很多错误而又肯评论赐教的话,感激不尽。

不多说了(讨厌C++的原因更多了)

此文可以配合《C++中#pragma pack(N)计算sizeof》一起看。

以下讨论在64位环境的前提下进行;

1.带有虚函数的类

class A
{
public:
    int a_;
    virtual void foo1() { cout << "A1" << endl; }
    virtual void foo2() { cout << "A2" << endl; }
    virtual void foo3() { cout << "A3" << endl; }
};

对于这种情况,当然是8字节的虚函数表指针+4字节的int类型大小,默认对齐系数下对齐后的大小为16;(对齐系数可以通前文说的修改#pragma pack(k)中的k值来改变)

2.普通地继承带有虚函数的父类

class B:public A
{
public:
	int b_;
	virtual void foo1() override { cout << "B1" << endl; }
	virtual void foo4() { cout << "B2" << endl; }
	virtual void foo3() override { cout << "B3" << endl; }
};

在这种情况下,子类与父类公用虚函数表和虚函数表指针,子类override的函数直接覆盖父类虚函数表中同名函数的位置,子类当中多出的函数放在虚函数表的最后,该类的大小为:虚指针8+父int4+子int4,默认对齐系数下对齐后得到结果为16;

3.虚继承下非完全覆盖父类的虚函数

class BB:virtual public A
{
public:
	int bb_;
	virtual void foo1() { cout << "BB1" << endl; }
	virtual void foo4() { cout << "BB2" << endl; }
	virtual void foo3() { cout << "BB3" << endl; }
};

在虚继承的情况下,子类当中会有一个虚基类表指针,指向虚基类表,非完全覆盖下,子类会有自己的虚函数表指针,所以该类的大小为:BB虚函数表指针8+虚基类指针8+BB的int变量4+偏移4+A的虚函数表指针8+A的int变量4+偏移4,默认对齐系数下对齐后大小为40;(在C++对象模型中,虚继承而来的派生类会生成一个隐藏的虚基类指针(vbptr),在Microsoft Visual C++中,虚基类表指针总是在虚函数表指针之后)

4.虚继承下完全覆盖父类的虚函数

class C:virtual public A
{
public:
	int c_;
	virtual void foo1() { cout << "C1" << endl; }
	virtual void foo2() { cout << "C2" << endl; }
	virtual void foo3() { cout << "C3" << endl; }
};

在完全覆盖的情况下,子类不再拥有自己的虚函数表指针,所以和情况3相比,该类只是少了一个虚函数指针,于是大小为40-8=32;

5.虚继承下的菱形问题

class Ori
{
public:
	int a;
	virtual void foo1() { cout << "Ori" << endl; }
};
class V1:virtual public Ori
{
public:
	int v1;
	virtual void foo2() { cout << "V1" << endl; }
};
class V2 :virtual public Ori
{
public:
	int v2;
	virtual void foo3() { cout << "V2" << endl; }
};
class V3 :public V1, public V2
{
public:
	int v3;
	virtual void foo4() { cout << "V3" << endl; }
};

这里需要先说,内存布局的时候,依次是左一、左二...,也就是先V1,再V2,再是虚爷爷Ori。

类Ori的大小为16,类V1和V2都是40,对于类V3,因为虚继承机制,类Ori在V3中只有一个副本,尽管他从V1和V2间接继承了两次,所以V3的大小为:(V1虚函数表指针8+虚基类指针8+V1的int变量4)+(V2虚函数表指针8+虚基类指针8+V2的int变量4)+(V3的int变量4+偏移量4)+(A的虚函数表指针8+A的int变量4+偏移量4),默认对齐系数下对齐后为64,函数foo4被加到类V1的虚函数表的最后,跟普通继承差不多

6.多重虚继承下类的大小

class A
{
public:
	int a_;
	virtual void foo1() { cout << "A1" << endl; }
	virtual void foo2() { cout << "A2" << endl; }
	virtual void foo3() { cout << "A3" << endl; }
};//16
class B:virtual public A
{
public:
	int b_;
	virtual void foo1() { cout << "B1" << endl; }
	virtual void foo4() { cout << "B2" << endl; }
	virtual void foo3() { cout << "B3" << endl; }
};//40
class A2
{
public:
	int a2_;
	virtual void foo1() { cout << "A21" << endl; }
	virtual void foo2() { cout << "A22" << endl; }
	virtual void foo3() { cout << "A23" << endl; }
};//16
class C:public A2
{
public:
	int c_;
	virtual void foo1() { cout << "C1" << endl; }
	virtual void foo2() { cout << "C2" << endl; }
	virtual void foo3() { cout << "C3" << endl; }
};//16
class D :virtual public B,virtual public C
{
public:
	virtual void foo1() { cout << "D1" << endl; }
	virtual void foo2() { cout << "D12" << endl; }
	virtual void foo3() { cout << "D13" << endl; }
	virtual void foo10() { cout << "D13" << endl; }
};

由上面的规则,我们可以知道,类A的大小为16,类B的大小为40,类A2的大小为16,类C的大小为16,对于类D,由于foo10在前面的虚函数表当中都找不到,所以类D自己有一个虚函数表指针,当发生多重虚继承时,和多个虚函数一样,虚基类指针只有一个,编译器会通过偏移量去访问不同的虚基类,所以类D也只有一个虚基类指针,因此D的大小为:D虚函数表指针8+虚基类指针8+B的大小+C的大小 = 72;

7.多重继承下类的大小

class E :public B, public C
{
public:
	virtual void foo1() { cout << "D1" << endl; }
	virtual void foo2() { cout << "D12" << endl; }
	virtual void foo3() { cout << "D13" << endl; }
	virtual void foo10() { cout << "D13" << endl; }
};

相比于情况6,普通的多重继承少了一个虚基类表指针和一个虚函数表指针,虚函数表会多重继承的第一个父类公用,覆盖的就直接覆盖,多出的就加到虚函数表最后,所以大小上为:(B虚函数表指针8+虚基类指针8+B的int变量4)+(C虚函数表指针8+C的int变量4)+(A的虚函数表指针8+A的int变量4+偏移量4)= 48


我看有的文章,直接继承是把父类的大小加子类的变量。我感觉应该不是这样吧,直接继承的时候,父类的偏移量应该不算吧。具体我也不知道,先记录到这里,后面我去看看内存布局。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++虚继承下的类大小 的相关文章

  • 06.5 Code

    06 5 Code 推力 force 推力的应用旋翼的气动阻力空气阻力矩滚转力矩电机的转速 推力 force span class token comment force 61 电机的转速 xff5c 电机的转速 xff5c xff08 带
  • C、C++ 对于char*和char[]的理解

    1 char 和char 的共同点 都是指针 xff0c 指向第一个字符所在的地址 2 char 的用法 char a 61 34 aaa 34 char p1 61 a char 是常量指针 xff08 常量的指针 xff09 xff0c
  • 重新抛出(rethrow)

    有可能单个catch不能完全处理一个异常 在进行了一些校正行动之后 xff0c catch可能确定该异常必须由函数调用链中更上层的函数来处理 xff0c catch可以通过重新抛出 rethrow 将异常传递给函数调用链中更上层的函数 重新
  • 4-2 图像聚类算法

    4 2 图像聚类算法 目录1 分类与聚类1 1 分类1 2 聚类1 3 聚类样本间的属性1 4 聚类的常见算法 2 K Means聚类2 1 概念2 2 步骤2 3 例子2 4 K Means聚类与图像处理2 5 K Means聚类优缺点优
  • JavaWeb-03 统一字符集编码、JSP的页面元素、JSP九大内置对象-request

    1 使用Eclipse开发Web项目 JSP项目 tomacat 2 在Eclipse中创建的Web项目 xff1a 浏览器可以直接访问WebContent中的文件例如 http 127 0 0 1 8888 MyJspProject in
  • 9-1 从零开始训练网络

    9 1 从零开始训练网络 目录1 搭建网络基本架构要完成的功能 2 构建训练网络1 实现网络训练功能2 获取训练数据及预处理 3 启动训练网络并测试数据 目录 搭建网络基本架构构建训练网络启动训练网络并测试数据 1 搭建网络基本架构 要完成
  • 基于知识图谱的推荐系统

    基于知识图谱的推荐系统 推荐系统 xff1a 核心目标是通过分析用户行为 兴趣 需求等信息 在海量的数据中挖掘用户感兴趣的信息 如商品 新闻 POI point of interest 和试题 等 个性化推荐算法是推荐系统的核心 其主要可以
  • mysql级联删除

    mysql级联删除 场景 xff1a 员工表 id xff1a 员工idleader id xff1a 该员工的领导的id xff08 也是员工id xff09 外键dept id xff1a 该员工的部门id xff08 部门表外键 xf
  • 【Seata】安装 - mac

    1 下载 官网 xff1a https seata io zh cn index html 2 修改配置文件 2 1 file conf 还有user password 2 2 registry conf 1 xff09 registry
  • Go Modules模式

    Go Modules模式 xff08 1 xff09 go mod 命令 命令作用go mod init生成 go mod 文件 在当前文件夹下初始化一个新的 go mod 文件go mod download下载 go mod 文件中指明的
  • 【Go】flag

    flag String span class token keyword func span span class token function String span span class token punctuation span n
  • Mybatis 逆向工程

    Mybatis 逆向工程 Maven项目generatorConfig xmlpom xml Maven项目 项目结构 xff1a generatorConfig xml span class token prolog lt xml ver
  • 原理分享 | 单片机常用通信协议汇总(上)

    vx 嵌入式工程师成长日记 https mp weixin qq com s biz 61 Mzg4Mzc3NDUxOQ 61 61 amp mid 61 2247484134 amp idx 61 1 amp sn 61 b779ccf0
  • C语言模拟TCP通信-------收发数据

    简介 这篇是我学习网络编程时初次接触到的 xff0c 感觉挺适合初学者 xff0c 下文主要介绍了如何使用Linux模拟TCP通信 xff0c 分为客户端和服务器端两大部分 xff0c 外加一个总的头文件 流程 服务器端和客户端使用TCP的
  • 多传感器融合记录

    多传感器信息融合的典型应用 多传感器融合中的时间硬同步1 论文阅读 weixin 39606911的博客 CSDN博客 前言阅读硕士论文 自动驾驶中多传感器集成同步控制器设计与实现 xff0c 该论文为自动驾驶设计了一套时间同步控制器 xf
  • VINS记录

    euroc launch lt launch gt lt arg name 61 34 config path 34 default 61 34 find feature tracker config euroc euroc config
  • OpenCV介绍与入门

    OpenCV入门 OpenCV介绍关于OpenCV1 OpenCV能做什么 xff1b 2 OpenCV与图形学与FFmpeg的关系 xff1b 3 OpenCV的未来 xff1b OpenCV介绍 OpenCV是计算机视觉的框架 关于Op
  • 【可见光室内定位】(一)概览

    目录 一 室内无线定位技术概况二 研究现状三 应用前景背景 一 室内无线定位技术概况 二 研究现状 得益于可见光通信 xff08 xff36 xff2c xff23 xff09 技术的迅速发展 xff0c 可 见光定位 xff08 xff3
  • 【机器学习中的数学】比例混合分布

    比例混合分布 Scale Mixture Distribution 混合分布是来自其他随机变量的集合构成的随机变量的概率分布 xff1a 一个随机变量是根据给定的概率从集合随机选取的 xff0c 然后所选随机变量的值就得到了 first a

随机推荐

  • 互联网相似图像识别检索引擎 —— 基于图像签名的方式

    一 引言 多媒体识别是信息检索中难度较高且需求日益旺盛的一个问题 以图像为例 xff0c 按照图像检索中使用的信息区分 xff0c 图像可以分为两类 xff1a 基于文本的图像检索和基于内容识别的图像检索 xff08 CBIR xff1a
  • 【Vim】使用map自定义快捷键

    map简介 map是一个映射命令 将常用的很长的命令映射到一个新的功能键上 map是Vim强大的一个重要原因 xff0c 可以自定义各种快捷键 xff0c 用起来自然得心应手 映射的种类 有五种映射存在 xff1a 用于普通模式 输入命令时
  • 【Scala】使用Option、Some、None,避免使用null

    避免null使用 大多数语言都有一个特殊的关键字或者对象来表示一个对象引用的是 无 xff0c 在Java xff0c 它是null 在Java 里 xff0c null 是一个关键字 xff0c 不是一个对象 xff0c 所以对它调用任何
  • 【Linux】使用update-alternatives命令进行版本的切换

    引言 在Debian系统中 xff0c 我们可能会同时安装有很多功能类似的程序和可选配置 xff0c 可能会出现同一软件的多个版本并存的场景 比如像是一些编程语言工具 xff0c 一些系统中自带的是python2 6 xff0c 而现在py
  • stm32G0 启动

    目的 STM32G是意法半导体这两年新推出的系列芯片 xff0c 相比原先的F系列的芯片有很多提升点 xff0c 将来必将取代F系列芯片的地位 对于新芯片的应用来说能够正确下载与运行程序是比较重要的一点 xff0c 这篇文章将对 STM32
  • 【scikit-learn】交叉验证及其用于参数选择、模型选择、特征选择的例子

    xfeff xfeff 内容概要 训练集 测试集分割用于模型验证的缺点K折交叉验证是如何克服之前的不足交叉验证如何用于选择调节参数 选择模型 选择特征改善交叉验证 1 模型验证回顾 进行模型验证的一个重要目的是要选出一个最合适的模型 xff
  • 【scikit-learn】网格搜索来进行高效的参数调优

    xfeff xfeff 内容概要 如何使用K折交叉验证来搜索最优调节参数如何让搜索参数的流程更加高效如何一次性的搜索多个调节参数在进行真正的预测之前 xff0c 如何对调节参数进行处理如何削减该过程的计算代价 1 K折交叉验证回顾 交叉验证
  • 【scikit-learn】评估分类器性能的度量,像混淆矩阵、ROC、AUC等

    xfeff xfeff 内容概要 模型评估的目的及一般评估流程分类准确率的用处及其限制混淆矩阵 xff08 confusion matrix xff09 是如何表示一个分类器的性能混淆矩阵中的度量是如何计算的通过改变分类阈值来调整分类器性能
  • 【Scala-ML】使用Scala构建机器学习工作流

    引言 在这一小节中 xff0c 我将介绍基于数据 xff08 函数式 xff09 的方法来构建数据应用 这里会介绍monadic设计来创建动态工作流 xff0c 利用依赖注入这样的高级函数式特性来构建轻便的计算工作流 建模过程 在统计学和概
  • 【Scala】响应式编程思想

    何为响应式编程 响应式编程是一种面向数据流和变化传播的编程范式 xff0c 数据更新是相关联的 这意味着可以在编程语言中很方便地表达静态或动态的数据流 xff0c 而相关的计算模型会自动将变化的值通过数据流进行传播 以响应式编程方式进行思考
  • 【函数式】纯函数与替代模型

    纯函数 一个函数在程序执行的过程中除了根据输入参数给出运算结果之外没有其他的副作用影响 xff0c 我们可以把这类函数称为 纯函数 纯函数由于不依赖外部变量 xff0c 使得给定函数输入其返回结果永远不变 xff0c 比如整数的加法函数 x
  • C/C++ 常用程序库

    C C 43 43 程序库 https zhuanlan zhihu com p 98056565 来几个不常见但是很变态的库吧 bundle 把几乎所有常见的压缩库封装成了一个库 接口完全统一 想用哪个用哪个 就一个h和一个巨TM大的cp
  • git 提交的时候报错:error: 'flutter_app/' does not have a commit checked out

    如果有朋友遇到这个问题 xff0c 请不用担心 xff0c 因为这个问题非常简单 xff01 出现的情况就是 xff0c 你在一个github仓库里面 xff0c 放进来一个文件夹 xff0c 但是文件夹里面还有文件夹 xff0c 而且还没
  • Fragment中使用viewLifecycleOwner/getActivity/this

    观察liveData使用生命周期 如图 xff1a 当liveData想在fragment里观察的时候 xff0c 可以使用getActivity this getViewLifecycleOwner getActivity不必说 xff0
  • Android的FileProvider使用解释

    前言 从Android7 0 xff08 N xff09 开始 xff0c 严格执行 StrictMode 模式 xff0c 也就是说 xff0c 将对安全做更严格的校验 不允许在 App 间 xff0c 使用 file 的方式 xff0c
  • STM32G0 串口编程

    利用USB转串口调试 xff0c 烧写单片机程序 1 打开STM32 CubeProgrammer软件设置启动配置 2 让程序从system Flash启动 xff08 1 xff09 勾选 DTR 勾选RTS 取消勾选RTS 2 取消勾选
  • Android使用SystemProperties基础了解

    安卓系统属性是以键值对的形式存在 xff0c 一般放在system prop xff0c build prop xff0c default prop等文件中 这些属性可能是进程状态 xff0c 资源使用情况 xff0c 系统特有属性等等 创
  • 使用Android Studio打包Module成jar包

    现在假设我们想打包一个module成jar包的形式给其它应用调用 xff1a vrservice 1 0 jar 步骤1 在Module项目的build gradle文件中做如下配置 xff1a 生成jar包的配置如下 xff1a def
  • C++无符号整型与有符号整型变量的运算-不简单

    示例分析 xff1a include lt iostream gt include lt stdio h gt struct Result char c char d unsigned char e Result getChar int x
  • C++虚继承下的类大小

    前言 带有虚函数的虚继承的对象模型比较复杂 xff0c 所以单独整理一下 其实关于计算类大小是C 43 43 的一大易错点之一 即便是我在这儿理了半天 xff0c 也不一定就是正确的 xff0c 如果大佬看到本文 xff0c 发现我很多错误