一、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