C++ 虚继承对象内存布局

c++对象模型05:虚继承内存布局 虚继承 虚继承解决了菱形继承中最派生类拥有多个间接父类实例的情况。虚继承的派生类的内存布局与普通继承很多不同,主要体现在: 虚继承的派生类,如果定义了新的虚函数,则编译器为其生成一个虚函数指针(vptr)以及一张虚函数表。该vptr位于对象内存最前面。(非虚继承时,派生类新的虚函数直接扩展在基类虚函数表的下面。) 虚继承的派生类有单独的虚函数表,基类也有单独的虚函数表,两部分之间用一个四个字节的0x00000000来作为分界。 虚继承的派生类对象中,含有四字节的虚基表指针。 在C++对象模型中,虚继承而 阅读详情

C++ 虚继承对象内存布局

一、先区分:普通继承 vs 虚继承

1. 普通公有继承(非虚)

struct Base { int a; };
struct Derived : Base { int b; };

布局:Base部分 + Derived自身成员

Derived 对象内存:
[ int a ] // Base
[ int b ] // Derived

多继承普通继承会多份基类副本,菱形继承产生二义性,所以引入虚继承 virtual,让所有派生类共享同一份公共基类(虚基类)。

2. 虚继承核心目的

解决菱形继承(钻石继承)的数据冗余、成员二义性问题:公共父类只存一份,派生类通过**虚基表指针(vbase ptr)**间接访问虚基类。

二、核心概念

  1. 虚基类(virtual base class):被 virtual 继承的上层父类
  2. 虚基表指针(vbptr, virtual base pointer):每个含有虚继承的派生类对象会带一个 vbptr,指向虚基表 vbtable
  3. 虚基表 vbtable:存储「当前对象 → 虚基类子对象」的内存偏移量
  4. 虚基类子对象统一放在派生类内存末尾,所有派生类共享同一份虚基类数据

区分:

  • vptr(虚表指针):虚函数用,存虚函数地址;
  • vbptr(虚基指针):虚继承用,存虚基类偏移。
    一个类可同时有 vptr + vbptr。

三、最简单路虚继承示例

#include <iostream>
using namespace std;

struct VBase { // 虚基类
    int v_a = 10;
};

struct Mid : virtual VBase { // 虚继承 VBase
    int m_b = 20;
};

Mid 对象内存布局(32/64位通用逻辑,以64位举例)

Mid 包含:

  1. vbptr(虚基指针,8字节64位)
  2. 自身成员 m_b(int 4,对齐补4)
  3. 末尾:共享虚基类 VBase 子对象 v_a

内存排布:

Offset  内容
0x00    vbptr  → 指向 Mid 的虚基表 vbtable
0x08    m_b    (int)
0x0C    填充对齐 4字节
0x10    VBase::v_a (虚基类,放在最后)

Mid 的虚基表 vbtable(vbptr指向这里)

虚基表存储从 vbptr 地址到虚基类子对象首地址的偏移:

  • 偏移值:0x10,代表从 vbptr 位置往后加 0x10 才能找到 VBase。

访问 mid.v_a 的底层流程:

  1. 取对象首地址,拿到 vbptr;
  2. 查虚基表得到偏移 offset;
  3. 对象首地址 + offset = VBase子对象地址;
  4. 访问 v_a。

四、经典菱形虚继承(最常考布局)

钻石结构:

        Top (虚基类,仅一份)
       /   \
virtual/     \virtual
     Left   Right
       \     /
        \   /
         Down

代码:

struct Top {
    int t = 1;
};

struct Left : virtual Top {
    int l = 2;
};

struct Right : virtual Top {
    int r = 3;
};

struct Down : Left, Right {
    int d = 4;
};

Down 对象完整内存布局(64位系统)

规则:

  1. 先放 Left 派生部分:vbptr_Left + Left::l
  2. 再放 Right 派生部分:vbptr_Right + Right::r
  3. 再放 Down 自身成员 d
  4. 内存最末尾:唯一一份 Top 虚基类 t
Offset  内容
0x00    vbptr_Left   // Left 的虚基指针
0x08    Left::l      // int 4
0x0C    对齐填充4
0x10    vbptr_Right  // Right 的虚基指针
0x18    Right::r     // int4
0x1C    对齐填充4
0x20    Down::d      // int4
0x24    对齐填充4
0x28    Top::t       // 全局唯一虚基类,所有类共享

两张虚基表

  1. Left 的 vbtable:存 Left 对象首地址到 Top 的偏移 0x28
  2. Right 的 vbtable:存 Right 对象首地址到 Top 的偏移 0x18
访问逻辑举例:

Down d; d.t;

  • 通过 Left 分支:d首地址 + Left虚基表偏移 → Top地址
  • 通过 Right 分支:d首地址 + Right虚基表偏移 → 同一个Top地址
    完美实现只有一份Top,无冗余、无二义。

五、同时含虚函数 + 虚继承(vptr + vbptr 共存)

struct VBase {
    virtual void func() {} // 虚函数,带vptr
    int a;
};
struct Der : virtual VBase {
    int b;
};

Der 对象布局:

  1. vbptr(虚基指针,虚继承用)
  2. Der::b
  3. 末尾:VBase子对象(内含 VBase 的 vptr + int a)
0x00 vbptr
0x08 b
0x10 VBase子对象:
    0x10 vptr(虚函数表指针)
    0x18 a

区分两个指针:

  • vbptr:属于派生类 Der,用于找虚基类 VBase;
  • vptr:属于虚基类 VBase 内部,用于多态虚函数调用。

六、关键底层规则总结(面试高频)

1. vbptr 生成规则

只要类使用 virtual 继承任意基类,该类对象就会增加一个vbptr;
多虚继承则对应多个 vbptr(菱形中Left、Right各一个)。

2. 虚基类存放固定规则

所有虚基类子对象统一放在派生类内存布局的最后,不会穿插在派生类成员中间。

3. 虚基表 vbtable 存储内容

虚基表里只存偏移量 offset:当前vbptr所在位置 → 虚基类子对象首地址的差值。
不同派生类、不同继承分支,偏移值不同。

4. 大小对比:普通多继承 vs 虚继承

以上面菱形为例:

  • 普通非虚多继承:Down 包含两份Top,内存更大,数据冗余;
  • 虚继承:仅一份Top,代价是每个中间类多一个 vbptr(指针开销)。

5. 构造/析构特殊规则(和布局配套考点)

  1. 虚基类构造最先调用:无论继承层级多深,虚基类构造函数第一个执行;
  2. 只有最底层派生类 Down 能直接初始化虚基类 Top,Left/Right 构造函数对Top的初始化会被忽略;
  3. 析构顺序相反:派生自身 → 中间类 → 虚基类。

七、32位 / 64位系统差异

  • 32位:指针4字节,vbptr/vptr 都是4字节,对齐按4;
  • 64位:指针8字节,对齐按8;
    布局结构逻辑完全一致,仅指针宽度、填充对齐不同。

八、常见面试问题简答

  1. 虚继承为什么能解决菱形二义性?
    虚基类只存一份,派生类通过虚基表偏移间接访问唯一副本,不存在多份数据冲突。
  2. vbptr 和 vptr 区别?
    vptr 对应虚函数表,存函数地址,实现多态;vbptr对应虚基表,存内存偏移,实现共享虚基类。
  3. 虚继承的开销是什么?
    每个含虚继承的子类多一个vbptr,间接访问虚基类成员需要查表计算偏移,性能略低于普通继承。
  4. 虚基类放在对象哪个位置?
    永远在派生类布局末尾,不会在前面。

在这里插入图片描述
在这里插入图片描述
布局规则,将基类成员变量放到最下面,然后用vbptr代替;
vbptr指向vbtable虚基类表 , 第一行是vbptr相对本身子对象的偏移

C++ 内存布局 - Part6: 虚继承 虚继承可以在菱形继承体系中,防止派生类中有多份重复祖基类内容。如下图所示,如果是常规继承,Class Final中会有两份Class Base的内容。通过虚继承,即Derived1 虚继承自Base, Derived2 也虚继承自Base, 那么Final中将最终保留一份Base部分的内容。 阅读详情

相关推荐

c++虚继承对象的内存布局

c++虚继承对象的内存布局               --VC7.1特别版网上关于c++对象布局的文章挺多,而且《深度探索c++对象模型》(Inside The C++ Object Model 侯捷 译)一书中也很详细地介绍。如果你一点都不了解C++对象的布局,我推荐你看看《深度探索c++对象模型》的第三章,如果你意犹未尽下面的两个系列都很不错:一是陈皓的《C++ 对象的内存布

BlueDog专栏 1万+

虚函数的复杂(继承)内存布局

继承关系一般分为:单继承和多继承,而多继承的出现就又导致了一种特殊的继承关系菱形继承,为了解决菱形继承的冗余和二义性,又有了菱形虚拟继承的解决方案。本篇主要介绍以上四种继承关系的类对象虚函数内存布局。

Codenange的博客 1113

C++虚继承深度解析:从菱形继承到对象内存布局

C++多继承在带来灵活性的同时,也常因菱形继承引发数据冗余与访问二义性等问题。虚继承作为C++对象模型中的关键机制,通过引入虚基类指针(vbptr)与虚基类表(vbtable),让多个派生分支共享同一份基类子对象,从根源上消除重复存储,并重新定义了构造与析构的规则。理解虚继承的内存布局,有助于开发者快速定位sizeof异常、构造顺序错乱、基类指针转换等疑难问题,对框架开发、插件系统及复杂类层次设计尤为重要。本文从菱形继承的痛点出发,逐层拆解虚继承的实现原理,辨析虚函数与虚继承的差异,并结合工程实践给出避坑清

weixin_32715349的博客 116

详解多态、虚继承、多重继承内存布局及虚表(C++)

详解C++多态、虚继承、多重继承内存布局,及虚函数表,虚表指针。

一枚菜鸡 2970

详谈C++中虚基类在派生类中的内存布局

今天重温C++的知识,当看到虚基类这点的时候,那时候也没有太过追究,就是知道虚基类是消除了类继承之间的二义性问题而已,可是很是好奇,它是怎么消除的,内存布局是怎么分配的呢?于是就深入研究了一下,具体的原理如下所示: 在C++中,obj是一个类的对象,p是指向obj的指针,该类里面有个数据成员mem,请问obj.mem和p->mem在实现和效率上有什么不同。 答案是:只有一种情况下才有重大差异,该情况必须满足以下3个条件: (1)、obj 是一个虚拟继承的派生类的对象 (2)、mem是从虚拟基类派生下来的成员 (3)、p是基类类型的指针 当这种情况下,p->mem会比obj.mem多了两个中间层

Linux - xarray 工具类

第三层节点直接与绿色的值绑定,若需要像数组一样通过下标访问绿色的值对象那么需要给每一个节点进行编码,这样通过根节点即可访问到具体的对象。对于未使用到的小数组不申请空间,这样只需设计一种方法将这些非连续的小数组在一个数据结构中组织在一起即可,对于这个数组结构需要具有结构紧凑并且能够快速定位到对应的小数组段上,这时候树形结构上场了。一个很大的数组必然可以由一个个小数组拼接而成,若定义一个最小粒度的数组长度,比如长度4,那么从宏观上看一个大数组就由一个个长度为4的小数组排队而成。

ymf604的专栏 1085

C++虚继承内存布局

虚继承

weixin_38255385的博客 659

Win10/Win11共享打印机0x00000709终极修复指南:从注册表到LPD协议的全面解决方案

本文提供了Win10/Win11共享打印机0x00000709错误的终极修复指南,涵盖从注册表修改到LPD协议的全面解决方案。通过详细分析错误根源、基础修复四步法、高阶LPD协议方案及企业级深度修复策略,帮助用户彻底解决打印机共享问题,提升局域网打印效率。

weixin_33724046的博客 312

c++虚继承对象的内存布局(修改版)

c++虚继承对象的内存布局(修改版)               网上关于c++对象布局的文章挺多,而且《深度探索c++对象模型》(Inside TheC++ Object Model 侯捷译)一书中也很详细地介绍。如果你一点都不了解C++对象的布局,我推荐你看看《深度探索c++对象模型》的第三章,如果你意犹未尽下面的两个系列都很不错: 一是陈皓的《C++ 对象的内存布局》图文并貌,

lingfengtengfei的专栏 1524

保姆级教程:Windows下用GDC-Client下载TCGA数据,从环境变量配置到成功下载

本文提供Windows平台下使用GDC-Client下载TCGA数据的完整教程,从环境变量配置到成功下载。详细介绍了GDC-Client工具的安装、系统环境变量设置、配置文件优化以及数据下载实战技巧,帮助生物信息学研究者高效获取TCGA数据库中的癌症基因组数据。

weixin_42520025的博客 404

【3D目标检测】激光雷达和相机联合标定(一)——ROS同步解包

激光雷达和相机联合标定(一)——ROS同步解包

qq_44703886的博客 4236

C++之对象内存布局(继承、虚函数、虚继承等多种情况)

目录 1. 对象内存布局基本情况 2. 在不同情况下的对象内存布局——通过实验验证 2.1 无继承 2.1.1 无虚函数 2.1.2 有虚函数 2.2 深度为1的多继承 2.2.1 无虚函数 1. 对象内存布局基本情况 (1)对象内存空间中存放的内容包括普通成员变量、虚表指针(如果类中包含虚函数)、虚基表指针(如果存在虚继承); (2)对象内存空间大小按结构体对齐原则来计算,若类是空类,则对象内存空间大小是1字节,以保证不同对象的内存地址不同;当空类作为基类时,对于派生类来说,其基类部分

Keep trying until you get it right 539

HyperLPR3车牌识别-Android-SDK光速部署与使用

基于深度学习高性能中文车牌识别 High Performance Chinese License Plate Recognition Framework.快速部署AndroidSDK

tunm 6121

C++虚继承内存布局解析:从菱形继承到对象内存模型

在面向对象编程中,多重继承是实现代码复用的重要机制,但其引发的菱形继承问题常导致数据冗余和访问二义性。C++通过虚继承技术解决这一难题,其核心原理是重构对象内存布局,引入虚基类指针实现共享基类子对象。从技术价值看,理解内存布局对调试诊断、序列化通信、性能优化至关重要,特别是在与C代码交互或处理跨进程数据时。虚继承通过将共享基类置于对象尾部,配合虚函数表中的偏移量信息,确保派生类中基类唯一性,但需注意其带来的间接访问开销。本文以菱形继承为切入点,结合调试器实操,深入剖析普通继承与虚继承下vptr、vbptr的

aoanping0730的博客 407

WPF实战:HelixToolkit 3D图形开发入门指南

本文详细介绍了如何使用HelixToolkit在WPF中进行3D图形开发,从环境搭建到创建第一个3D程序(如彩色立方体),再到进阶技巧和常见问题排查。通过实战案例和性能优化建议,帮助开发者快速掌握3D场景构建和模型导入技术,提升开发效率。

weixin_29197051的博客 761

基于YOLOv26的道路抛洒物检测系统原理实现

本文提出了一种基于YOLOv26的道路抛洒物智能检测系统,通过深度学习技术实现道路抛洒物的实时检测与分类。系统采用分层架构设计,包含视频采集、目标检测、分类评估和预警处理等模块,能够自动识别轮胎碎片、货物包装、金属零件等多种抛洒物,并根据危险程度分级预警。YOLOv26算法在检测精度和速度上的优势使其特别适合道路监控场景,结合背景建模和运动目标提取技术,有效提高了复杂道路环境下抛洒物的识别率。该系统可集成到现有智能交通监控平台,为道路安全管理提供实时决策支持。

一键难忘的博客 1258

C++对象模型:多重继承和虚继承的内存布局

这是一篇译文,原文地址:这里;  翻译的文章转自:点击打开链接     这篇文章主要讲解虚继承的C++对象内存分布问题,从中也引出了dynamic_cast和static_cast本质区别、虚函数表的格式等一些大部分C++程序员都似是而非的概念。原文见这里(By Edsko de Vries, January 2006)         敬告: 本文是介绍C++的技术文章,假定读者对

Sunshine的专栏 2459

EMD (经验模态分解) 在 C++ 中的实现指南

EMD 是由 Norden E. Huang 等人在1998年提出的一种适用于非线性和非平稳信号的分析方法。对称性:IMF 的上下包络对称。零交叉点:IMF 的零交叉点数量与极值点数量相同或相差一个。本文详细介绍了如何在C++中实现EMD,包括理论背景、算法步骤、代码实现和优化等内容。通过丰富的代码示例和详细的解释,希望能够帮助读者掌握EMD的基本原理,并能够在实际项目中灵活应用。未来,随着信号处理技术的不断发展,EMD及其变种(如EEMD、CEEMDAN等)将继续在各个领域发挥重要作用。

qq_38334677的博客 1750

C++虚继承内存布局解析:从菱形继承到对象模型实践

本文深入解析C++虚继承的内存布局,从菱形继承问题出发,详细探讨虚基类指针(vbptr)和虚基类表的运作机制。通过实战代码分析对象模型,揭示虚继承与虚函数表的交互原理,并提供性能考量与使用建议,帮助开发者优化内存使用和访问效率。

weixin_29001655的博客 207

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

【C++虚继承内存布局深度解析】:揭秘多继承下的对象模型与性能优化策略

深入解析C++虚继承的内存布局,揭示多继承下对象模型的底层机制与性能优化策略。涵盖虚基类指针布局、共享基类子对象处理及内存开销控制,适用于复杂继承体系设计。掌握高效实现方法,提升程序性能,值得收藏。

FastSolve的博客 419

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++对象内存布局:单继承,多继承,虚继承

1. 一个简单的例子class A{ int a=1; char b=2;//内存对齐 char c=3;//内存对齐 }; A instance;1.1 内存分布 地址 变量名 值 0x0093f9a0 instance - 0x0093f9a0 a 0x00000001 0x0093f9a4 b,c 0xcccc0302(-

归去来兮的博客 696

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

上一篇: C++ 类编译器默认生成的十大特殊成员函数
下一篇: 深度对比简单工厂模式和策略模式
越甲八千
博客等级 码龄11年 2959粉丝 553原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值