C++虚表撞上虚继承后的内存布局

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

先看一个有虚表的内存布局:

class  K
{
    double a;
    virtual void fun();
};
class L : public K
{
    int b;
    virtual void fun();
};//24
/*
        +---
        | +--- (base class K)
 0      | | {vfptr}
 8      | | a
        | +---
16      | b
        | <alignment member> (size=4)
        +---
*/

这个没啥说的应该都会,注意字节对齐就OK

再来看看加上虚继承呢?

class  A
{
    virtual void fun();
};
class B :virtual public A
{
    int b;
    virtual void fun();
};//12
/*
        +---
 0      | {vbptr}
 4      | b
        +---
        +--- (virtual base A)
 8      | {vfptr}
        +---
*/

在这里就纠正一下有些人的默认思维,总以为有了虚表,vfptr总是放在这个类开始地址处,从上面我就能看见并不是这样。
你可能会说:vfptr 的优先级是不是没有 vbptr高啊?
其实并不是这样 vfptr的优先级是比vbptr高的,只是有了虚继承,自己类的东西在内存上会放在最上面,而父类的东西就放在下面了,这样做的好处是啥?就是为了不重复公共父类的成员变量。
注意:这里B类没有自己的vfptr,和父类共用一个vfptr

再来看一个菱形继承内存布局:

class  O
{
    int a;
};
class P : virtual public O
{
    int a;
};
class R : virtual public O
{
    int a;
};
class S : public P, public R
{
    int a;
};//24
/*
        +---
        | +--- (base class P)
 0      | | {vbptr}
 4      | | a
        | +---
        | +--- (base class R)
 8      | | {vbptr}
12      | | a
        | +---
16      | a
        +---
        +--- (virtual base O)
20      | a     这就是公共父类,在子类中只有一份,节约了内存
        +---
*/
class  O
{
    int a;
};
class P : virtual public O
{
    int a;
};
class R : virtual public O
{
    int a;
};
class S :virtual public P, virtual public R
{
    int a;
};//24
/*
+---
0      | {vbptr}    //如果子类也是虚继承那么每个类都是并行的关系
8      | a
+---
+--- (virtual base O)
16      | a
+---
+--- (virtual base P)
24      | {vbptr}
32      | a
+---
+--- (virtual base R)
40      | {vbptr}
48      | a
+---
*/

然后验证我说的vfptr优先级比vbptr高:

class  G
{
    int a;
    virtual void fun();
};
class H : virtual public G
{
    int b;
    virtual void fun22();
};
/*
        +---
 0      | {vfptr}                   //自己的虚表
 4      | {vbptr}
 8      | b
        +---
        +--- (virtual base G)
12      | {vfptr}                   //父类的虚表
16      | a
        +---
*/

是不是vfptr优先级比vbptr高啊?在这种情况下虚表指针还是处于类的前四个字节。
为什么会出现这样的情况呢?虚继承需要有自己的虚表,当自己类中出现和父类不一样的虚函数就需要创建一个属于自己的虚表,并且还要继承父类的虚表。这方面也能说明一个类的虚表可以有多个。而且是有和父类不一样的虚函数才产生的。

有人会问为啥要两个虚表?一个不行么?
再看两个例子

class  C
{
    int a;
};
class D :public C
{
    int b;
};
class E :public D
{
    int c;
};
/*
        +---
        | +--- (base class D)
        | | +--- (base class C)
 0      | | | {vfptr}
 4      | | | a
        | | +---
 8      | | b
        | +---
12      | c
        +---
*/

这个是普通继承子类是包含于父类的,当然父类的vfptr我们可以利用,没必要两个vfptr

but:

class  C
{
public:
    int a;
};
class D :virtual public C
{
public:
    int b;
};
class E :virtual public D
{
public:
    int c;
};
/*
+---
0      | {vbptr}
8      | c
+---
+--- (virtual base C)
16      | a
+---
+--- (virtual base D)
24      | {vbptr}
32      | b
| <alignment member> (size=4)
| <alignment member> (size=4)
+---
*/

如果是虚继承,子类虚继承的父类和自己是并列的关系,并不是包含。由于是并列关系,所以vfptr可能有多个。
为啥不是嵌套类型?就要说到虚继承是如何节约内存的啦。就是为了公共父类不被重复创建(想想菱形继承),所以才每个类占一个块,这样就不可能在子类中产生两个一样的父类了。
那你可能会说为啥不共用一个呢?
我的理解是:这里的确没有节省内存,但是虚继承的公共父类成员变量节省了内存啊,并列和包含关系只能存在一种啊,虚继承用了并列,肯定也有一点点瑕疵嘛(或许我的水平还达不到,占时察觉不到具体是为什么,如果你有你的见解,希望你能给我留言)

可能大家还有个疑问,这两个虚表指针里面存放的是什么东西?

父类的虚函数是存放在第一个虚表里还是第二个虚表里?
当我们调用父类的虚函数时,用的是子类的第一个vfptr还是第二个vfptr?

#include<iostream>
#include<fstream>
using namespace std;

class  X
{
public:
    virtual void test(){
        cout << "test X" << endl;
    }
};
class Y :virtual public X
{
public:
    /*virtual void test(){
        cout << "test Y" << endl;
    }*/
    virtual void test2(){
        cout << "test2 Y" << endl;
    }
};

int main(){
    Y *x = new Y;
    typedef void(*P)();
    ((P)(*(int *)*(int *)(x)))();
    ((P)(*(int *)*( (int *)(x) + 2 )))();
    system("pause");
}
/*
test2 Y //这里可以看出来 第一个 vfptr 里面放的是不同于父类的虚函数
test X  //这里可以看出来 第二个 vfptr 里面放的是父类同名的虚函数
        //如果上面代码去掉注释  第二个打印的就是 test Y
*/

typedef void (*p)() 如果看不懂请看我另一篇blog http://blog.youkuaiyun.com/program_anywhere/article/details/53040756

大东辛苦创作O(∩_∩)O哈哈~

C++知识积累:继承关系(含虚函数)下类的内存布局 目录 1 无继承 2 一般继承 2.1 单继承 2.2 多层继承 2.3 多重继承 2.4菱形继承 3 虚继承 3.1 虚继承的布局 3.2 虚继承的多层继承 3.3 虚继承的菱形继承 总结 1 无继承 类A定义如下: class A { private: int A_1; public: static int A_2; int A_3; ... 阅读详情

相关推荐

C++虚继承与普通继承的区别

虚继承的时候在子类的对象中会多出一个叫虚类指针的大小,有的资料说这个指针指向的内存里面包含了该子类的偏移量和到基类的距离。但是我跟踪过这段内存,发现里面的数据没有规律,也找不到更多的支撑材料,权且先知道子类的对象里面会有这么一个东西吧。 先总结虚拟继承中比较特殊的地方,希望能够对大家有所帮助: 虚继承时子类的虚函数不再是添加到父类部分的虚表中,而在普通的继承中确实直接添加到父类的虚表中,这就意

bama2488313716的专栏 4263

虚继承为什么不会出现浪费内存猜想

一直想不通为什么看人家画内存图都会把虚拟成员放到最后,如何通过虚表确定虚拟变量唯一的呢? 大概是在初始化之前编译器已经根据代码分配好了内存,如上,B和C都虚继承于A,也许D继承于B,C ,这样D的内存分布图其实已经编译器大致构画好了。a的变量也许不在最下面,反正在一个位置吧;然后从下依次往上是d的变量,c,b,初始化的时候反着来,额b和c对于a的偏移量怎么确定的啊!暂且还是放在最下面吧公共变量,反正B,C在D中有个先后顺序,于是对于编译器在生成内存时,遇到vtural继承,比如B中有一个intC,中有一个.

m0_46615101的博客 228

C/C++:从内存布局理解C++虚继承的实现原理

准备 测试环境:Visual Studio 2017 Visual Studio提供给用户显示C++对象在内存中的布局的选项: /d1reportSingleClassLayoutXXX     // XXX替换为类的名字 /d1reportAllClassLayout  创建工程取名为research,在research中找到research源文件 右键该文件点击属性 在r...

无聊科技 1766

虚继承以及虚函数对象的内存大小

关于虚继承的介绍可以看这个网课菱形继承,下面我将介绍虚继承后对象大小的问题 #include <iostream> using namespace std; class Animal{ public: int m_Age; }; class Sheep : virtual public Animal{}; class Camel : virtual public Animal{}; class Sheep_Camel : public Sheep, public Camel{}; i

Jesse_Liu666的博客 312

C++虚函数、虚表与虚继承:从内存布局到多态底层原理

多态是面向对象编程的基石,而C++中实现多态的关键机制就是虚函数与虚表。很多开发者能熟练使用virtual关键字,却未必清楚vptr在对象构造过程中的切换规则,也不明白虚继承与虚函数本质上是两套独立机制。理解这些底层原理,不仅能解释为什么要将基类析构函数声明为virtual,也能避免菱形继承带来的二义性与资源泄漏。本文从动态绑定与静态类型的区别讲起,结合对象内存布局,剖析虚函数表的结构、多继承下的vptr分布,以及虚继承如何通过虚基类表解决重复子对象问题。掌握这些知识,能帮助你在实际工程中避开析构非虚、构造

weixin_30225755的博客 319

普通继承与虚继承关系下派生类的内存布局

普通继承 基类、派生类中都不含所有虚函数 例: #include<iostream> using namespace std; class A{ public: A(int a):ma(a){} void Show() { cout<<"A::Show()"<<endl; } protected: int ma; }; class...

灲咲的博客 507

虚继承与空基类优化

参考文章 虚继承与空基类优化 。 class TEST_X {}; // 1 class TEST_Y : public virtual TEST_X { // 12 virtual void fun() {}; // 虚表4 //指虚基类的表指针4 char c; }; // 字节对齐 4 // 空基类优化为0 class TEST_Z : public TEST_X { // 8 virtual void fun() {}; // 空基类优化为 0 ch

专注于C/C++/Linux领域创作 204

COMSOL多物理场仿真:瓷绝缘子电热耦合建模与优化策略

本文详细介绍了使用COMSOL Multiphysics进行瓷绝缘子电热耦合仿真的完整流程与优化策略。文章阐述了电热耦合效应的工程意义,并逐步讲解了从几何建模、材料定义、物理场设置到边界条件与网格划分的关键步骤。重点分析了如何通过焦耳热耦合实现电场与温度场的双向交互,以及如何利用仿真结果进行参数化优化和瞬态故障模拟,为提升电力设备可靠性提供有效的数字化设计工具。

n8m7b6v5c4的博客 882

虚拟继承和虚表

普通继承和虚拟继承:

Commander 1262

Linux单独编译设备树,开发者分享 | Petalinux 工程中设备树的介绍

本文来自XILINX嵌入式产品应用工程师 Terry Ni设备树是 Petalinux kernel 的关键组件,接下来以 2020.1 版本为例,为大家介绍一下在Xilinx Petalinux 工程中的设备树是如何产生,配置以及修改的。Petalinux 工程中设备树的位置当我们创建了一个 petalinux 工程后(以zcu102为例),在编译的时候,会自动生成对应的设备树文件。这些设备树的...

weixin_28877107的博客 2580

【HAL库-STM】智能温控按键小风扇【胎教级教学】

硬件设备:DHT11,0.96寸oled屏,二/三/四线风扇(不挑),面包板 、f10的精英板等等。当人在的风扇面前时可以自行控制风扇,不在面前时又想开风扇就让他温控自行开启,(在这里有一个想法就是加上蓝牙和wifi控制的功能能够远程遥控,这个如果做的再全一点就要自主研发是一个多功能app,这里没有做。针对这个dht11,我一开始是卡在了这个check函数里面,一直是我给完dht11低电平和高电平后,dht11不回复我低电平和高电平,这个不是我的问题,当时代码是没毛病的,这个点一直卡着我。吐血五天实现小成品

weixin_52451652的博客 1738

虚继承详解及其内存分布

什么是虚继承? 根据百度百科: 虚继承是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。 如上图: 假设类a是父类,b类和c类都继承了a类,而d类又继承了b和c,那么由于d类进行了两次多重继承a类,就会出现两份相同的a的数据成员或成员函数,...

SanfordZhu的博客 5031

CPU也能跑大模型?手把手教你用vLLM离线部署Qwen2.5-7B-Instruct(附性能优化技巧)

本文详细介绍了如何在CPU服务器上高效部署Qwen2.5-7B-Instruct模型,通过vLLM框架和一系列优化技巧实现离线推理。内容包括环境配置、模型加载优化、性能调优及生产环境部署方案,帮助开发者在资源有限的情况下也能运行大语言模型。

weixin_28691741的博客 489

一文读懂C++虚继承的内存模型

一文读懂C++虚继承的内存模型1、前言2、多继承存在的问题3、虚继承简介4、虚继承在标准库中的使用5、虚继承下派生类的内存布局解析6、总结 1、前言 C++虚继承的内存模型是一个经典的问题,其具体实现依赖于编译器,可能会出现较大差异,但原理和最终的目的是大体相同的。本文将对g++中虚继承的内存模型进行详细解析。 2、多继承存在的问题 C++的多继承是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。从概念上来讲这是非常简单的,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突

游戏行业资深搬砖人员 1万+

从三个角度理解知识图谱

2012年谷歌首次提出“知识图谱”这个词,由此知识图谱在工业界也出现得越来越多,对于知识图谱以及相关概念的理解确实也是比较绕。自己在研究大数据独角兽Palantir之后开始接触知识图谱,也算对其有了一定了解,这里从三个角度总结一下怎么去理解知识图谱。 几个基本概念:本体、实体、知识库、知识图谱 1、本体是概念的集合,是大家都公认的概念框架,一般不会改变如“人”、“事”、“物”、 “地”、“组...

JavaChaoCo的博客 6901

ThinkPad T14 Gen 2与T14P系列对比

表格您的需求推荐机型日常办公、出差、文档处理、视频会议✅(够用、轻便、便宜)视频剪辑、3D建模、编程编译、AI训练、高色准设计✅(性能强、屏幕好、扩展佳)想要未来 5 年不换机✅T14p预算低于 5000 元✅T14 Gen 2(或考虑 T14 Gen 3/4)T14p 2025 款 ≠ 小改款,而是架构级升级它从“高性能笔记本”进化为“AI 移动工作站”,核心差异在于NPU + Ultra 平台 + 全面屏显 + 超长续航。如果您从事AI 开发、视频创作、工程仿真、多任务编程。

tecoes的博客 2736

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

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

一枚菜鸡 2969

OpenClaw-遇到的问题-Health check failed: gateway closed (1006 abnormal closure (no close frame)): no clos

OpenClaw网关服务安装成功但运行异常,显示健康检查失败(错误代码1006)。系统提供了Web控制界面(http://127.0.0.1:18789/)和带令牌的安全访问链接。文档建议参考故障排除指南,并提供了可选应用扩展功能(包括移动端支持)。注意运行本地代理存在安全风险,建议加固系统配置。若需启用网络搜索功能,需配置Brave搜索API密钥。安装完成后会自动打开仪表盘,需保持浏览器标签页以控制OpenClaw服务。

直到世界的尽头 1万+

【C++拾遗】 从内存布局看C++虚继承的实现原理

准备工作 1、VS2012使用命令行选项查看对象的内存布局 微软的Visual Studio提供给用户显示C++对象在内存中的布局的选项:/d1reportSingleClassLayout。使用方法很简单,直接在[工具(T)]选项下找到“Visual Studio命令提示(C)”后点击即可。切换到cpp文件所在目录下输入如下的命令即可       c1 [filename].cpp

上善若水,人淡如菊 2万+

在 SAP S/4HANA Public Cloud 中做产品扩展:从 Key User 到 ABAP Cloud 与 BTP 的三轨实践指南

摘要:SAP S/4HANA Public Cloud 提供三种扩展方式(key user、developer extensibility 和 side-by-side),遵循 clean core 原则确保升级稳定。key user 扩展适合无代码轻量改良,developer extensibility 用于受控 ABAP 开发,side-by-side 则适用于云原生独立应用。文章通过销售订单优先级字段、退货审批微服务和采购校验等案例,说明不同扩展路径的适用场景与实现方法,帮助开发者在三系统景观中合理选

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 293

精雕软件(JDpaint) 5.5 绿色版

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

C++在gcc下的单继承,多继承,虚继承的内存布局

一览本文目的gcc下不同继承方式的存储模型单继承单继承无重写:单继承重写多重继承多继承无重写:多继承重写:虚继承多重继承无法解决的模型真正的菱形继承尾语 本文目的     最近看了《深入探索c++对象模型》,被其中关于虚函数的各种继承的内存结构,绕来绕去,十分纠结。并且在《深入探索c++对象模型》这本书中的的各种结构是大体的策略,可以用来理解,但是gcc编译器中的具体实现比较详细。而网...

Lupin 3057

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

C++继承的内存布局

单一继承 可见以下几个方面: 1)虚函数表在最前面的位置。 2)成员变量根据其继承和声明顺序依次放在后面。 3)在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新。 多重继承 我们可以看到: 1) 每个父类都有自己的虚表。 2) 子类的成员函数被放到了第一个父类的表中。 3) 内存布局中,其父类布局依次按声明顺序排列。 4) 每个父类的虚表中的f()函数都被overw...

Waves___的博客 4322

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C++ 虚表(Vtable)和虚基表(Vbtale)与 虚函数 和 虚继承

虚基表(Vbtale)**是通过编译器的实现来支持虚函数和虚继承的机制。它们通过特定的数据结构在内存中存储,并且可以通过一些方法可视化或理解它们在内存中的布局。在虚继承中,当一个类被多个基类继承时,多个基类会共享一个虚基类的实例。虚基表是用来处理虚继承的,尤其是用于指向虚基类子对象的指针。每个包含虚函数的类都会有一张虚表,它存储了该类的所有虚函数的指针。每个包含虚函数的类会有一个虚表,它存储了指向该类虚函数的指针。的成员函数,它允许通过基类的指针或引用调用派生类中的重写函数,而不是调用基类的函数。

m0_74374651的博客 1272

【C++精讲系列】五分钟带你彻底搞懂虚函数,虚表和虚继承

在 C++ 中,虚函数几乎是不可避免的工程工具,而虚继承几乎永远不该成为默认选择。前者为运行期多态付出了一点可以接受的成本,后者则是在继承结构已经失控时,用复杂度换语义正确性的补救机制。本文从虚函数与虚表出发,拆解它们如何改变对象的内存布局和调用路径,并进一步分析多继承与虚继承为何会迅速推高复杂度。

fdxghb65467的博客 782

虚继承内存布局,构造顺序

转自:http://www.cnblogs.com/ziolo/archive/2013/05/07/3066022.html 一直疑惑虚继承与虚函数并存时类的内存结构,下面的文章虽然格式乱但是值得细细品味,我在vs2010下查看了下类的内存结构,除了没有“0x00000000  // 虚基类的非虚基类的分隔符”外,内存结构基本与文中所述一样,另外补充一下虚基类的非虚基类A的偏移地址由8

433

从内存布局看C++虚继承的实现原理

今天讲的是虚基类和虚继承,我们就先用上面介绍的命令提示工具查看一下普通多继承子类的内存布局,可以跟后文虚继承子类的内存布局情况加以比较。 我们新建一个名叫NormalInheritance的cpp文件,输入一下内容。/** 普通继承(没有使用虚基类) */ // 基类A class A { public: int dataA; }; class B :

xyx1311的博客 558
上一篇: 深入剖析C++中的三种new
下一篇: STL空间配置器之内存池技术
帅东
博客等级 码龄11年 676粉丝 88原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值