news 2026/9/4 22:02:53

C++菱形继承:从二义性到虚基表寻址的解析过程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++菱形继承:从二义性到虚基表寻址的解析过程

摘要:本文围绕 C++ 继承体系展开,先介绍多继承的基本概念与二义性处理方式,再分析菱形继承带来的二义性和数据冗余问题,进而引出菱形虚拟继承的解决方案,说明其通过虚基表与偏移量寻址来保证单个对象内基类成员只有一份。最后总结继承关系要点,并澄清虚基表共享与对象数据共享之间的常见误区。

目录

一、多继承

二、菱形继承

二义性问题

数据冗余问题

三、菱形虚拟继承

四、总结

五、误区总结


​​​​​​​一、多继承

多继承:一个派生类有两个或两个以上的直接基类,称为多继承。出现二义性可以用指定类型访问来解决。

二、菱形继承

菱形继承会带来二义性和数据冗余问题

二义性问题

可以通过显示指定类型访问,但是数据冗余还没有解决

class A { public: int _a=1; }; class B : public A { public: int _b=2; }; class C : public A { public: int _c=3; }; class D : public B, public C { public: int _d = 4; }; int main() { D d; d._a = 5;//出现二义性,B中有_a,C中有_a,编译器不知道调用哪个_a }

数据冗余问题

数据冗余是派生类对象模型有基类的成员数据重复出现,造成数据冗余,浪费空间。

三、菱形虚拟继承

为了解决菱形继承的二义性和数据冗余问题,C++引入了菱形虚拟继承的概念。虚拟继承确保基类的共享部分只有一份,避免了重复继承。

四、总结

继承关系

单继承:一个派生类只有一个直接基类时为单继承

多继承:多个派生类继承同一个基类,称为多继承

菱形继承:两个派生类继承同一个基类,又有某个类继承这两个派生类

菱形继承总结

菱形继承是两个派生类继承同一个基类,又有某个类继承这两个派生类,但是菱形继承会出现数据冗余二义性的问题,虽然可以通过指定类型访问解决二义性的问题,但是数据冗余问题还没解决,所以可以使用菱形虚拟继承来解决二义性数据冗余的问题,保证一个对象内只存一份基类成员。

菱形虚拟继承的原理是,派生类不会继承基类的成员变量,而是派生类的对象内存中有虚基表指针虚基表指针指向虚基表,虚基表里面存放的是偏移量,可以通过指针找到偏移量,通过偏移量+自己的地址找到基类成员。

虚机表本质/虚基类成员寻址/虚继承

虚基表是类级别的静态元数据,不属于对象。同一个类的所有对象共用唯一一张虚基表,表里存储的是编译期就确定的固定偏移常量,程序运行全程只读不变。
虚基类成员的寻址公式是:成员真实地址 = 当前对象首地址 + 虚基表偏移量
虽然所有对象的偏移寻址规则完全一致,但每个对象的内存首地址相互独立,所以访问到的都是自己内部独有的虚基类成员,对象之间数据完全隔离、互不干扰。
虚继承的核心作用,只是解决单个对象内部菱形继承带来的基类多副本和二义性问题,保证一个对象内只存一份基类成员它不会让多个对象共用同一份数据,修改某个对象的成员,只会改动当前实例内存,不影响其他对象,也不会修改虚基表。

五、误区总结

在学习 C++ 菱形虚继承内存模型时,容易混淆 虚基表共享 和 对象数据共享。误以为:多个对象共用同一张虚基表、同一套偏移量,就会共用同一份基类成员数据。

实际上是共用虚基表、共用寻址规则,只是共用一套计算公式,不共用内存数据。

误区 1:认为多个 D 对象会共用同一份 _a 成员
误以为:所有对象共用同一张虚基表、同一个偏移量,所以所有对象共享同一个 _a,改一个全变。
实际上不共享。虚继承只解决单个对象内部的成员冗余,不会跨对象共享数据。
误区 2:误以为虚基表存储对象的绝对内存地址
误以为:虚基表里存着 _a 的地址,所以多个对象会定位到同一块内存。
实际上:虚基表只存编译期固定偏移常量,不存任何地址、不存任何数据。
误区 3:误以为创建越多对象,虚基表会新增、扩容偏移项
误以为:d、d1 是不同对象,虚基表需要存储多个不同偏移量对应不同对象。
实际上:虚基表属于类级别,不属于对象。无论创建多少个实例,一张表、一组偏移量,永久不变。
误区 4:核心根源误区
只看到:虚基表相同、偏移量相同、虚基指针相同,每个对象的起始内存地址完全独立

在学习 C++ 菱形虚继承内存模型时,容易混淆 虚基表共享 和 对象数据共享。 误以为:多个对象共用同一张虚基表、同一套偏移量,就会共用同一份基类成员数据。 class A{ public: int _a; }; // 虚继承 A class B: virtual public A { public: int _b; }; // 虚继承 A class C :virtual public A { public: int _c; }; // 菱形继承:同时继承 B、C class D : public B , public C { public: int _d; }; int main() { D d; D d1; return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 21:57:41

Linux 性能调优与追踪完整篇:ftrace、perf、eBPF从采样到落地验证

CPU 被打满却说不清热点、延迟尖刺只能「重启试一下」、上线后回归全靠猜——缺的不是参数列表,而是 可复现的观测路径:从 tracepoint/kprobe 到采样火焰图,再到改参与回归对比。本文把 ftrace、perf_event、eBPF/bpftrace、常见调优开关与排…

作者头像 李华
网站建设 2026/9/4 21:57:13

Spec as AIOS:高德如何用统一规范控制AI代码熵增

AI专家杨夕凯现任职高德地图,担任智能应用与基建平台负责人,先后负责动态数据、AI 导航、智能应用及基建平台等业务方向。其长期深耕地图数字孪生、空间智能、AR/数字人及 AI 导航算法模型等领域,并参与建设支撑千亿级数据的端云一体化基础设…

作者头像 李华
网站建设 2026/9/4 21:55:21

Rocky Linux部署Hermes Agent+Web-UI:自托管AI工作台从0到1

1. 写在前面:为什么我盯上了这套组合先把话说清楚,这篇不是拿官方文档翻译一遍的流水账,而是我在真实服务器上从零部署 Rocky Linux Hermes Agent Hermes-Web-UI 的完整记录。如果你正在头疼这三件事:Hermes Agent 怎么在 RHEL …

作者头像 李华
网站建设 2026/9/4 21:54:58

基于STM32与LD3320的智能家居语音控制系统开源实战

我做了个大半年才敢拿出来说事的开源项目:基于STM32的智能家居语音控制系统。代码、原理图、仿真工程全部打包开源,不是那种只放截图不放工程的项目,所有文件都能直接打开使用。这篇文章我会把整个项目的设计思路、硬件选型、代码实现、仿真搭…

作者头像 李华
网站建设 2026/9/4 21:54:50

生产质量报表几十份,为什么还是追不上质量问题根源

导语 生产质量报表数量多但无法快速定位质量问题根源,核心原因是不同报表指标口径不统一,数据不一致干扰根因分析方向,反复核数拉长质量问题响应时间,最终增加不良损失,统一数据标准和指标口径是提升质量管理分析效率的…

作者头像 李华