從 C 到 C++:封裝的代價
C 語言中數據與操作分離;C++ 引入類,將數據與成員函數封裝。關鍵問題:封裝是否增加對象佈局成本?
- 非靜態數據成員:直接內含於對象中,同 C struct,無額外空間開銷。
- 靜態數據成員:存放於所有對象之外,單一實例。
- 非靜態成員函數:不出現在對象中,每個類僅有一份代碼;inline 函數可能在每個使用模塊產生實體。
- 虛函數:引入
vptr 指向 vtbl(虛函數表),增加指針大小的空間,間接調用帶來微小時間開銷。
- 虛基類:確保多重繼承中基類單一共享實體,增加指針/偏移量開銷。
三種對象模型演進
- 簡單對象模型:對象只存指向成員的指針槽,成員本身放在別處。統一槽大小,但間接訪問效率低,未實際採用。
- 表格驅動模型:所有成員信息抽離到數據成員表與成員函數表,對象存兩個表指針。函數表概念被虛函數表借鑑。
- C++ 對象模型 (Stroustrup):非靜態數據內含,靜態/函數成員外置,虛函數通過 vptr+vtbl 實現。兼顧空間與效率。
虛函數機制詳解
- 每個含虛函數的類生成一個
vtbl(靜態陣列),存虛函數地址。
- 每個對象含一個
vptr 指向其類的 vtbl。
- 構造/析構/拷貝賦值自動設定/重置
vptr。
type_info 通常放在 vtbl 首槽,支持 RTTI。
多重繼承下的額外負擔
Derived 轉換到第二或後續 Base 可能需調整 this 指針(偏移量),且虛基類需額外間接層。
總結
C++ 對象模型在不犧牲 C 效率的前提下實現封裝、繼承、多態。主要開銷來自虛函數與虛基類,但這是為了運行時多態與共享基類的必要代價。理解模型有助於優化與調試。