一、C++对象模型核心原理
1. 对象内存布局基础原理
设计哲学:
- 零开销原则:不为未使用的特性付出代价(如无虚函数则无vptr)
- 兼容性:C结构体在C++中保持相同内存布局
- 多态支持:通过虚函数表实现运行时动态绑定
内存布局实现机制:
编译器处理步骤:
- 成员排列:严格按声明顺序排列
- 偏移量计算:
offset = (previous_offset + previous_size + alignment - 1) & ~(alignment - 1)
- 填充插入:在成员间插入padding满足对齐
- 尾部填充:确保结构体大小为最大对齐值的整数倍
class Simple {char c; // 1字节 (align=1)int i; // 4字节 (align=4)double d; // 8字节 (align=8)
};
内存布局图示:
┌───────┬─────────────┬─────────────┬───────────────────────┐
│ char │ Padding │ int │ double │
│ (1B) │ (3B) │ (4B) │ (8B) │
│ 0x00 │ 0x01-0x03 │ 0x04-0x07 │ 0x08-0x0F │
└───────┴─────────────┴─────────────┴───────────────────────┘
总大小:16字节(1 + 3 + 4 + 8)
2. 虚函数机制原理(vtable/vptr)
vtable创建过程:
编译器为每个含虚函数的类创建虚函数表:
class Base {
public:virtual void f1() {} // vtable[0]virtual void f2() {} // vtable[1]
};
内存中的vtable:
Base vtable:
┌──────────────┬──────────────┐
| 0x00: &f1 | 0x08: &f2 | // 函数指针
└──────────────┴──────────────┘
vptr初始化原理:
对象构造序列(编译器隐式插入代码):
Derived* obj = new Derived();
// 编译器插入的隐式操作:
obj->__vptr = Derived::vtable; // 设置vptr
Base::Base(obj); // 基类构造
Derived::Derived(obj); // 派生类构造
关键点:
- vptr在构造函数的最早阶段设置
- 基类构造可能修改vptr(但派生类构造会重置)
- 析构过程反向操作:
~Derived() → ~Base() → vptr重置为Base vtable
动态绑定原理:
Base* pb = new Derived();
pb->f1(); // 动态调用Derived::f1()
编译后的机器码:
; x86-64示例
mov rax, [rbx] ; 加载vptr (rbx=对象地址)
mov rax, [rax] ; 加载vtable[0] (f1的函数指针)
call rax ; 间接调用
3. 继承模型原理
单继承内存布局:
class Base { int a; };
class Derived : public Base