C 語言無內置 OO 支持,但可通過結構體、函數指針、手動虛表實現完整對象系統(如 GTK GObject、COM、Linux 內核 kobject)。本文系統梳理核心機制。
struct Object { void* vtable; void* data; };
`vtable` 指向接口表(虛表),`data` 指向具體數據。單繼承時 `vtable` 即類虛表;多接口時對象含多個 `vtable` 指針或指向接口表數組。
struct IExample {
struct Class* klass; // 反向指針,指向類元數據
int (*method)(struct Object* this, int arg);
};
接口 = 函數指針集合 + 類反向指針。對象通過 `interface(obj)->method(obj, arg)` 調用。
struct Class {
int id; size_t obj_size;
struct Class* parent;
void (*init)(Class*);
void (*ctor)(Object*, void*);
void (*dtor)(Object*);
// ... 其他元操作
};
存儲對象大小、繼承鏈、構造/析構函數、接口表指針等運行時信息。
C 無編譯期元編程,類初始化必須在運行時顯式調用:
void MyClass_init() {
declare(&MyClass, &BaseClass); // 記錄繼承、計算對象大小
MyClass.init = MyClass_init;
MyClass.ctor = MyClass_ctor;
MyClass.dtor = MyClass_dtor;
struct IExample* iface = implement(&MyClass, &IExample);
iface->method = MyClass_method;
mixup(&MyClass, &MyMixin); // 混入實現
register_class(&MyClass); // 註冊到類型系統
}
主程序啟動時需手動調用所有 `XXX_init()`,極其繁瑣。解決方案:源碼分析工具自動生成初始化調用代碼。
// 用戶分配內存 Object* obj = malloc(MyClass.obj_size); object_init_at(obj, &MyClass); // 設置 vtable、調用 ctor // 系統分配 Object* obj = object_new(&MyClass); // 內部 malloc + init // 銷毀 object_destroy(obj); // 調用 dtor、free
IExample* interface(Object* obj) {
for (itable in obj->klass->itables)
if (itable->id == IExample_ID) return itable->vtable;
return NULL;
}
遍歷對象類的接口表數組,找到對應接口虛表。接口表含反向指針 `klass`,可實現接口間轉換(QueryInterface 風格)。
struct Object { void (*vfunc)(void* data); void* data; };
// 構造/析構為專用函數
Object* Foo_new() { Object* o = malloc(sizeof(Object)); o->vfunc = Foo_impl; o->data = malloc(...); return o; }
void Foo_delete(Object* o) { free(o->data); free(o); }
無虛表、無接口、無繼承,僅保留「數據+行為」綁定。構造/析構函數封裝生命周期。
C 實現 OO 核心三件套:對象結構(vtable+data)、類元數據、動態初始化。虛表實現多態,接口表實現多接口,Mixup 替代多重繼承。最大痛點是樣板代碼繁多,需代碼生成工具輔助。輕量場景下,簡化模型(單函數指針+專用構造/析構)更實用。