对象模型与内存布局实现分析
C++ 对象模型定义了对象在内存中的布局方式。本文分析 GCC 和 LLVM 的对象模型实现。
一、核心概念
1.1 对象内存布局
对象内存布局:
简单类:
┌─────────────────────────────────────┐
│ 成员变量 1 │
├─────────────────────────────────────┤
│ 成员变量 2 │
└─────────────────────────────────────┘
带虚函数的类:
┌─────────────────────────────────────┐
│ vptr(虚函数表指针) │
├─────────────────────────────────────┤
│ 成员变量 1 │
├─────────────────────────────────────┤
│ 成员变量 2 │
└─────────────────────────────────────┘1.2 空基类优化(EBO)
空基类优化:
没有 EBO:
┌─────────────────────────────────────┐
│ 空基类(1 字节) │
├─────────────────────────────────────┤
│ 派生类成员 │
└─────────────────────────────────────┘
有 EBO:
┌─────────────────────────────────────┐
│ 派生类成员(空基类不占空间) │
└─────────────────────────────────────┘二、GCC vs LLVM 差异对比
┌────────────────────────┬──────────────────────┬──────────────────────┐
│ 特性 │ GCC (libstdc++) │ LLVM (libc++) │
├────────────────────────┼──────────────────────┼──────────────────────┤
│ vptr 位置 │ 对象起始地址 │ 对象起始地址 │
│ EBO │ 支持 │ 支持 │
│ packed 结构 │ __attribute__((packed))│ __attribute__((packed))│
│ aligned 结构 │ alignas │ alignas │
│ 最小大小 │ 1 字节 │ 1 字节 │
└────────────────────────┴──────────────────────┴──────────────────────┘三、Itanium ABI
3.1 布局规则
Itanium ABI 布局规则:
1. 基类子对象按声明顺序排列
2. 虚基类放在最后
3. vptr 通常在对象起始地址
4. 对齐填充按最大成员对齐四、最佳实践
对象模型使用指南:
1. 注意对象大小:
· 使用 sizeof 检查
· 避免不必要的填充
2. 使用 EBO:
· 空基类不占空间
· 适合 policy-based 设计
3. 注意对齐:
· 使用 alignas 控制对齐
· SIMD 数据需要对齐
4. 避免 UB:
· 不要 reinterpret_cast 对象
· 使用 std::launder 访问新对象延伸阅读
- 虚函数表实现 — vtable 的实现
- RTTI 与 dynamic_cast — RTTI 的实现
- C++ ABI 深度解析 — ABI 的详细解释