C 語言雖無內建類與繼承,但通過結構體內存佈局規則與巨集技巧,可實現語意上的繼承、封裝與多態基礎。本文從內存模型出發,逐步推導兩種繼承實現方式、方法封裝策略、虛函數表雛形,並討論工程落地的取捨。
C++ 中子類物件包含父類子物件,內存連續分布:
[父類成員][子類自有成員] ^ ^ | | 父類指針 子類指針(同一地址)
因此 Child* 隱式轉換為 Parent* 只需地址不變,直接訪問前段內存。
struct Parent { int x, y, z; };
struct Child { struct Parent parent; float f; char s[10]; };
訪問父類成員需 child.parent.x 或強轉 ((struct Parent*)&child)->x,語法冗長,非直觀。
struct Child { int x, y, z; float f; char s[10]; };
訪問直接 child.x,但維護災難:父類增減欄位需同步修改所有子類。
#define PARENT_FIELDS int x; int y; int z
struct Parent { PARENT_FIELDS; };
struct Child { PARENT_FIELDS; float f; char s[10]; };
多層繼承同理:
#define CHILD_FIELDS PARENT_FIELDS; float f; char s[10]
#define GRAND_FIELDS CHILD_FIELDS; double d
struct Grand { GRAND_FIELDS; };
C 無成員函數,慣用「帶類型前綴的自由函數」模擬:
typedef struct Parent Parent;
void parent_method(Parent* self) { ... }
typedef struct Child Child;
void child_method(Child* self) {
parent_method((Parent*)self); // 子類調用父類方法,需顯式強轉
// 子類自有邏輯
}
為支援運行時多態,可在結構體內嵌函數指針表指針:
struct Parent_VTable {
void (*method1)(Parent*);
void (*method2)(Parent*, int);
};
const struct Parent_VTable parent_vtable = { parent_method1, parent_method2 };
struct Parent {
PARENT_FIELDS;
const struct Parent_VTable* vptr;
};
// 初始化
Parent* p = malloc(sizeof(Parent));
p->vptr = &parent_vtable;
// 調用
p->vptr->method1(p);
子類繼承時複製父類 vptr 並覆寫特定槽位,即構成簡易虛表機制。
private/protected 訪問控制、this 隱式參數、編譯器級多態、RTTI、異常處理。Type_method)、結構體扁平化+巨集維護、關鍵多態點引入虛表指針。