從 C++ 轉回 C 開發時,最讓人不適應的是失去了封裝、繼承、多態等語言級支持。但 C 語言靈活的結構體、函數指針、前向聲明等特性,足以構建出功能完備的面向對象框架。本文作為系列首篇,深入講解如何在 C 中實現封裝與繼承,並給出典型代碼模式。
C++ 中用 private/protected 隱藏細節,C 中通過不透明指針模式實現:
abc.h(公開頭文件):僅聲明公開結構體 struct st_abc、公開方法指針、構造/析構函數聲明。abc_p.h(私有頭文件):定義私有結構體 struct st_abc_private 完整內容。abc.c:包含 abc_p.h,實現所有方法,客戶代碼只包含 abc.h,完全看不見私有細節。/* abc.h - 公開接口 */
struct st_abc_private; // 前向聲明,隱藏定義
struct st_abc {
int a; // 公開數據成員
void (*xyz_func)(struct st_abc *); // 公開方法
struct st_abc_private *priv; // 私有數據指針
};
// 構造/析構函數聲明
struct st_abc *new_abc();
void delete_abc(struct st_abc *);
/* abc_p.h - 私有定義,僅 abc.c 可見 */
struct st_abc_private {
int hidden_data;
char *buffer;
// 任意內部實現細節
};
/* abc.c - 實現 */ #include "abc.h" #include "abc_p.h" #includestatic void xyz_func_impl(struct st_abc *this) { // 可直接訪問 this->priv->hidden_data } struct st_abc *new_abc() { struct st_abc *obj = malloc(sizeof(struct st_abc)); obj->priv = malloc(sizeof(struct st_abc_private)); obj->a = 0; obj->xyz_func = xyz_func_impl; // 初始化 priv 成員... return obj; } void delete_abc(struct st_abc *obj) { if (obj) { free(obj->priv); free(obj); } }
abc.h,內部重構不影響客戶編譯。C++ 繼承本質是基類成員在派生類對象中的固定偏移佈局。C 中通過結構體包含模擬:
struct st_base {
int base_data;
void (*base_method)(struct st_base *);
};
struct st_derived {
struct st_base base; // 必須作為第一個成員
int derived_data;
void (*derived_method)(struct st_derived *);
};
這樣 struct st_derived * 強轉為 struct st_base * 時地址不變,可直接調用基類方法,實現「子類指針隱式轉換為父類指針」。
基類函數指針作為「虛函數表」雛形,派生類構造時填入具體實現,調用時通過對象指針間接調用,即實現運行時多態。下一篇將詳細展開。
本文確立了 C 語言面向對象編程的兩大基石:
下一篇將深入講解多態的完整實現(虛函數表模擬)、純虛類/接口模式,並給出綜合示例代碼。