C++对象模型(11)-- 虚基类

news2025/3/12 22:12:35

1、虚基类的引入

我们再来复习一下“多重继承的对象布局”,假设继承的类结构是这样的:

相应的代码:

class W {
public:
    int i_w;
};
class X : public W {};
class Y : public W {};
class Z : public X, public Y {};

我们在main()函数中加入如下代码:

Z z;
cout << z.i_w << endl; //这句话编译会报错

系统提示i_w的访问不明确。为什么呢?

因为定义Z对象时,W类会被继承2次,1次是Z --> X --> W,1次是Z --> Y --> W。虚基类的提出就是为了解决这种重复继承的问题,让W类只被继承1次。

虚基类的代码如下:

class W {
public:
    int i_w;
};
class X : virtual public W {};
class Y : virtual public W {};
class Z : public X, public Y {};

2、虚基类表、虚基类表指针

我们把前面虚基类的代码修改一下:

using namespace std;

class W {
public:

};
class X : virtual public W {};
class Y : virtual public W {};
class Z : public X, public Y {};

int main()
{
    cout << sizeof(W) << endl;
    cout << sizeof(X) << endl;
    cout << sizeof(Y) << endl;
    cout << sizeof(Z) << endl;
}

输出如下:

为什么输出的结果是:1、4、4、8呢?

这里要引入2个概念:1、虚基类表 vbtable 2、虚基类表指针 vbptr。

虚基类表指针占4个字节,类X、Y各含1个虚基类表指针,类Z继承这2个虚基类表指针,所以Z对象占8个字节。

3、虚基类中的对象数据布局

为了更好地观察虚基类中的对象数据布局,我们往每个类中都增加1个成员变量。

class W {
public:
    int i_w;
};
class X : virtual public W {
public:
    int i_x;
};
class Y : virtual public W {
public:
    int i_y;
};
class Z : public X, public Y {
public:
    int i_z;
};

(1) cl /d1 reportSingleClassLayoutX ch08.cpp

虚基类表指针占4个字节,i_x占4个字节,i_w占4个字节,所以类X的对象占12个字节。

类X对象数据布局:

(2) cl /d1 reportSingleClassLayoutY ch08.cpp

类Y对象的数据布局跟类X对象类似。

(3) cl /d1 reportSingleClassLayoutZ ch08.cpp

类Z对象数据布局:

4、虚基类带虚函数时的对象布局

4.1 类W有虚函数(其他类都没虚函数)

virtual void w_h() { cout << "W::w_h()" << endl; }

这时类X的对象成员布局是这样的:

类Z的对象成员布局是这样的:

从图中可以看到,这个时候虚函数表指针vptr在子对象w的头部。

4.2 类X有虚函数(类W、X都有虚函数)

virtual void x_h() { cout << "X::x_h()" << endl; }

这时类X的对象成员布局是这样的:

类Z的对象成员布局是这样的:

4.3 类Z有虚函数 (类W、X也有虚函数)

virtual void z_h() { cout << "Z::z_h()" << endl; }

这时的对象成员布局和4.2中一样:对象Z和X共用同一个虚函数表指针。

4.4 类Z有虚函数(其他类都没虚函数)

此时Z类对象的对象布局如下:

从上面的结果看,虚基类的对象布局有如下规律:

(1)在孙子类Z的对象布局中,爷爷类(W类)的数据总是位于最后位置。

(2)父类X、Y都有一个虚基类表指针。

(3)仅爷爷类W有虚函数时,它的虚函数表指针在W类对象的头部。

(4)仅X或Y类有虚函数时,虚函数表指针就在X或W类对象的头部;在Z类对象中,虚函数表指针也位于对象的头部。

比如当Y有虚函数,其他类都没有虚函数时,类Z对象的对象布局如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1104166.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BUUCTF题解之[极客大挑战 2019]EasySQL 1

1.题目分析 考查sql注入的基本使用。 1.sql注入的定义 SQL注入是一种针对Web应用程序的攻击技术&#xff0c;通过在应用程序的用户输入参数中嵌入SQL代码&#xff0c;进而攻击应用程序的数据库。 攻击者可以通过SQL注入来获取敏感信息、执行无权执行的操作、甚至完全控制数据…

tomcat的部署以及优化

tomcat的介绍 Tomcat的简介 Tomcat 是 Java 语言开发的&#xff0c;Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器&#xff0c;是 Apache 软件基金会的 Jakarta 项目中的一个核心项目&#xff0c;由 Apache、Sun 和其他一些公司及个人共同开发而成。 Tomcat 属于轻量…

Go项目踩坑:go get下载超时,goFrame框架下的go项目里将vue项目的dist同步打包发布,go项目打包并压缩

Go项目踩坑&#xff1a;go get下载超时&#xff0c;goFrame框架下的go项目里将vue项目的dist同步打包发布&#xff0c;go项目打包并压缩 go get下载超时goFrame打包静态资源vue项目打包gf pack生成go文件 静态资源使用打包发布go项目交叉编译&#xff0c;省略一些不必要的信息通…

黑客利用人工智能窃取医疗数据的 7 种方式

人工智能被描述为医疗保健行业的一把双刃剑。基于人工智能的系统可以分析大量数据并在早期和可治疗的阶段检测疾病&#xff0c;它们可以比任何人类更快地诊断症状&#xff0c;并且人工智能正在帮助药物开发&#xff0c;使新的救命药物得以识别并将其推向市场速度更快且成本显着…

三集合容斥原理整理

三集合容斥原理的三个公式都是怎么被推导出来的&#xff1f; - 刘明哲的回答 - 知乎 https://www.zhihu.com/question/465008307/answer/2251909478 你必须知道的行测数量知识&#xff08;七&#xff09;容斥问题 - 跟我考公吧的文章 - 知乎 https://zhuanlan.zhihu.com/p/4271…

基于若依框架的药品管理系统

若依框架每张表共有的五个属性&#xff1a; 更改若依后端代码时创建新的module&#xff0c;选择maven&#xff0c;继承ruoyi。 创建实体类时继承BaseEntity&#xff0c;这个类有创建人&#xff0c;创建时间等五个字段&#xff0c;这个类在其它模块中&#xff0c;需要在depende…

在.Core中用EF添加数据库实体类

首先安装dotnet-ef工具&#xff0c;否则提示&#xff1a; *无法执行&#xff0c;因为找不到指定的命令或文件。 可能的原因包括: *你拼错了内置的 dotnet 命令。 *你打算执行 .NET Core 程序&#xff0c;但 dotnet-ef 不存在。 你打算运行全局工具&#xff0c;但在路径上找不到…

SNAP对Sentinel-1预处理

SNAP对Sentinel-1预处理 一、导入数据 二、轨道校正 点击run开始处理 三、噪声去除 打开S-1 Thermal Noise Removal工具 如果选中了VH&#xff0c;就只会输出一个VH极化结果 四、辐射定标 Run 五、滤波处理 六、地形校正 这边的dem需要自己下载 dem下载地址 如果一格…

Orleans的成员管理和故障检测故障检测

Orleans的成员管理和故障检测故障检测 简介 Orleans框架是一个基于.NET平台的开源分布式系统框架&#xff0c;用于开发可扩展&#xff0c;高可用&#xff0c;高性能的云服务应用程序。它采用了Actor模型&#xff0c;将分布式系统中的各个节点抽象成为Actor&#xff0c;使得开…

Linux入门---页表的理解

目录标题 第一次认识页表第二次认识页表如何看待页表页表的大致构成 第一次认识页表 我们第一次认识页表是在介绍地址空间的时候&#xff0c;我们知道操作系统将内存划分为好几个区域&#xff0c;比如说栈区&#xff0c;堆区&#xff0c;未初始化区&#xff0c;已初始化区&…

力扣刷题 day47:10-17

1.位1的个数 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 方法一&#xff1a;逐个判断 利用n&1 #方法一&#xff1a;逐个…

重载、重写(覆盖)与重定义(隐藏)

重载、重写&#xff08;覆盖&#xff09;与重定义&#xff08;隐藏&#xff09; 重载隐藏&#xff08;重定义&#xff09;多态&#xff1a;重写&#xff08;覆盖&#xff09; 三者的区别 重载 必须是在一个作用域&#xff0c;函数名相同&#xff0c;参数不同&#xff08;个数不…

C复习-基础知识

参考&#xff1a; 里科《C和指针》Bryant, Hallaron 《深入理解计算机系统》何昊&#xff0c;叶向阳《程序员面试笔试宝典》 从hello.c到可执行文件hello 在Unix系统中&#xff0c;从源文件到目标文件的转化是由编译器驱动程序完成的&#xff1a; root> gcc -o hello hel…

RTOS(4)自己的第一个FreeRTOS程序

创建两个任务 什么是任务呢&#xff1f; 对于整个单片机程序&#xff0c;我们称之为application&#xff0c;应用程序。 使用FreeRTOS时&#xff0c;我们可以在application中创建多个任务(task)&#xff0c;有些文档把任务也称为线程 (thread)。 void Task1Function(void *p…

课时4作业3

Description 某人想将手中的一张面值100元的人民币换成10元、5元、2元和1元面值的票子。要求换正好40张&#xff0c;且每种票子至少一张。问&#xff1a;有几种换法&#xff1f; Input 无输入 Output 一个数&#xff0c;表示共有多少种换法 Sample Input 1 无 Sample O…

手写一个PrattParser基本运算解析器1: 编译原理概述

点击查看 基于Swift的PrattParser项目 编译原理概述 编译原理是我们每一个程序猿必须要了解的技能, 编译原理实际上并没有啥高深的技术, 我们如果在做业务开发, 也很少会用到编译开发的知识, 但是编译原理又是我们必备的基础知识之一. 所以我们需要对编译原理的内容有一个大概的…

76.C++ STL list容器

目录 1.什么是list容器 2.list构造函数 3. 元素插⼊和删除操作 4.大小操作 5.赋值操作 6.数据存取操作 7.反转、排序 1.什么是list容器 list 是 C 标准库提供的双向链表容器。它与 vector 和 deque 不同&#xff0c;不是连续的内存块&#xff0c;而是由节点组成的链表结…

C语言——二周目——数据在内存中的存储

目录 一、整数的存储方式 二、浮点数的存储方式 一、整数的存储方式 因为CPU只有加法器&#xff0c;所以对于整型来说&#xff0c;数据在内存中通常采用补码的方式进行储存。 在这里复习一下原码、反码、补码。 正数和无符号数的原码、反码、补码相同&#xff1b; 负数的原…

考察软件开发公司的能力

当公司需要与软件外包公司合作时需要考察软件开发公司的能力和水平&#xff0c;这会涉及到很多方面的因素。需要通过综合考察和了解软件开发公司的能力和水平&#xff0c;选择合适的合作伙伴&#xff0c;确保项目的成功交付。下面分享一些关键步骤和方法&#xff0c;希望对大家…

【JVM】JVM的垃圾回收机制

JVM的垃圾回收机制 对象死亡判断方法引用计数算法可达性分析算法 垃圾回收算法标记清除法复制算法标记整理算法分代算法 Java运行时内存的各个区域,对于程序计数器,虚拟机栈,本地方法栈这三个部分区域而言,其生命周期与相关线程有关,随线程而生,随线程而灭,并且这三个区域的内存…