怎么计算一个类的大小 (C++)

怎么计算一个类的大小 (C++)

怎么计算一个类的大小 (C++)


文章目录


图文版

在这里插入图片描述

文字版

这是一个非常经典的C++面试题。在C++中,计算一个类的大小并不是简单地将成员变量的大小相加,而是涉及内存对齐虚函数继承等多个因素的影响。

以下是计算类大小的核心规则和详细步骤:

核心结论速览

  1. 非静态成员变量 是影响大小的主体。
  2. 静态成员变量 不属于类实例,存在全局数据区,不计入sizeof
  3. 成员函数 存在代码段,不计入sizeof
  4. 虚函数:只要类有虚函数(无论是1个还是多个),类实例中就会有一个虚函数表指针(vptr),在64位系统下通常占8字节,32位下占4字节
  5. 内存对齐:编译器为了提高访问效率,会在成员之间或末尾填充字节,使变量存储在其倍数地址上,并使类总大小是最大对齐数的整数倍。
  6. 继承
    • 空类大小为1字节(为了占位,表示对象存在)。
    • 派生类的大小一般是基类成员 + 派生类新成员,同时也要满足对齐规则。

详细计算步骤与示例

1. 基础计算:空类
classEmpty{// 没有任何成员};
  • 大小1字节
  • 原因:编译器需要给这个类的对象分配一个独一无二的地址,所以会隐式插入1个字节。
2. 只有成员变量(考虑内存对齐)

对齐规则:

  1. 第一个成员在偏移量为0的位置。
  2. 后续成员要放到该成员大小的整数倍上。
  3. 最终类的大小要是最大成员大小的整数倍。
classExample1{char c;// 1字节int i;// 4字节};
  • 分析
    • c 占偏移0。
    • i 占4字节,需要从4的倍数(偏移4)开始存。因此,在 c 后面会有3个字节的填充
    • 此时已占:1© + 3(填充) + 4(i) = 8字节。
    • 最大对齐数是4,8是4的倍数,无需末尾填充。
  • 结果8字节(而不是直觉上的 1+4=5)。
3. 有虚函数(引入虚表指针)
classBase{int a;virtualvoidfunc(){}};
  • 分析
    • int a:4字节。
    • 虚函数表指针(vptr):64位系统下8字节。
    • 内存对齐:最大对齐数是8(vptr)。a 从偏移0开始占4字节,为了放8字节的vptr,需要在 a 后面填充4字节,使vptr从偏移8开始。
    • 总大小 = 4(a) + 4(填充) + 8(vptr) = 16字节。
  • 结果16字节
4. 继承关系
classA{int a;// 4char b;// 1// 实际A的大小:对齐到4,b后面填充3字节,A为8字节};classB:publicA{int c;// 4char d;// 1};
  • 分析
    • 先放基类A的部分:8字节。
    • 再放派生类B的 c:偏移8(刚好是4倍数),占4字节,总偏移12。
    • 再放 d:偏移12,占1字节,总偏移13。
    • 对齐收尾:B中最大对齐数是4(int),当前总大小13不是4的倍数,所以末尾填充3字节到16。
  • 结果16字节
5. 特殊情况:虚继承

虚继承涉及虚基类指针或类似机制(编译器实现不同,但通常会增加额外开销),用来解决菱形继承问题。

classA{int x;};// 4字节,对齐后可能8?classB:virtualpublicA{int y;};
  • 结果:通常比普通继承多出一个指针的大小(8字节),用来定位虚基类子对象。情况比较复杂,取决于编译器实现。

总结清单

计算类的大小时,可以按以下清单排查:

  1. 基本大小:列出所有非静态成员变量的大小。
  2. 虚函数检查:如果有 virtual 函数(包括继承来的),加上虚表指针的大小(8/4字节)。
  3. 虚继承检查:如果有虚继承,加上虚基类指针的开销(通常也是8/4字节)。
  4. 内存对齐
    • 计算每个成员相对于起始地址的偏移,插入填充
    • 最后让总大小是最大对齐数的整数倍,末尾填充

简记:

类的大小 = 非静态成员变量大小之和 + 虚函数/虚继承指针开销 + 内存对齐填充字节。

总结

这篇文章是作者搜集大量面经和资料这里出来的。感谢你的支持
作者wkm是一名中国矿业大学(北京) 大一的新生,希望得到你的关注
如果可以的话,记得一键三联!

Read more

【Linux系统编程】(四十)线程控制终极指南:从资源共享到实战操控,带你吃透线程全生命周期

【Linux系统编程】(四十)线程控制终极指南:从资源共享到实战操控,带你吃透线程全生命周期

前言         在 Linux 多线程开发中,“线程控制” 是贯穿始终的核心技能 —— 从线程的创建、终止,到等待、分离,每一步操作都直接影响程序的性能、稳定性和资源利用率。而要熟练掌握线程控制,首先必须理清一个关键问题:进程和线程究竟哪些资源共享、哪些资源独占?这是理解线程控制逻辑的底层基石。         很多开发者在编写多线程程序时,常会陷入这样的困境:明明调用了pthread_create却创建失败,线程退出后出现资源泄漏,用pthread_join等待线程却始终阻塞,甚至因误操作导致整个进程崩溃。这些问题的根源,往往是对线程与进程的资源关系理解不深,或是对 POSIX 线程库的控制接口使用不当。         本文将从 “进程与线程的资源划分” 入手,层层递进讲解 Linux 线程的完整控制流程 —— 包括 POSIX 线程库的使用、线程创建、终止、等待、分离等核心操作,全程结合实战代码和底层原理,用通俗的语言拆解复杂概念,让你不仅 “会用” 线程控制接口,更能 “懂原理”

By Ne0inhk
从 .NET 到 Java 的转型指南:详细学习路线与实践建议

从 .NET 到 Java 的转型指南:详细学习路线与实践建议

文章目录 * 第一部分:转型背景与核心差异分析 * 1.1 为什么需要从 .NET 转型到 Java * 1.2 .NET 与 Java 核心架构差异 * 1.2.1 运行时环境对比 * 1.2.2 内存管理机制 * 1.3 心态调整与学习策略 * 1.3.1 相似性利用 * 1.3.2 差异性重视 * 第二部分:Java 语言基础深入学习 * 2.1 Java 语法核心概念 * 2.1.1 基本数据类型与包装类 * 2.1.2 字符串处理 * 2.

By Ne0inhk
Java 部署:Jenkins Pipeline 构建 Java 项目(自动化)

Java 部署:Jenkins Pipeline 构建 Java 项目(自动化)

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Java部署这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * Java 部署:Jenkins Pipeline 构建 Java 项目(自动化) 🚀 * 为什么选择 Jenkins Pipeline?🔧 * 环境准备:搭建 Jenkins 服务器 ⚙️ * 使用 Docker 快速启动 Jenkins * 安装必要插件 * 示例 Java 项目:一个简单的 Spring Boot 应用 🌱 * 项目结构 * `pom.xml` * `DemoApplication.java` * `HelloController.java` * 单元测试(可选但推荐) * 编写 Jenkins

By Ne0inhk
Java 大视界 -- 5230 台物联网设备时序数据难题破解:Java+Redis+HBase+Kafka 实战全解析(查询延迟 18ms)(438)

Java 大视界 -- 5230 台物联网设备时序数据难题破解:Java+Redis+HBase+Kafka 实战全解析(查询延迟 18ms)(438)

Java 大视界 -- 5230 台物联网设备时序数据难题破解:Java+Redis+HBase+Kafka 实战全解析(查询延迟 18ms)(438) * 引言: * 正文: * 一、技术选型:务实为王,拒绝炫技 * 1.1 核心技术栈选型对比 * 1.2 选型核心原则(10 余年实战经验总结) * 二、架构设计:闭环为王,层层兜底 * 2.1 整体架构图 * 2.2.1 生产设备层(数据源头) * 2.2.2 边缘网关层(数据预处理) * 2.2.3 消息接入层(数据缓冲) * 2.

By Ne0inhk