C++ 單繼承下,派生類對象內存 = 基類子對象 + 派生類自有成員。基類部分在低地址,派生類部分緊隨其後。因此,基類指針指向派生類對象時,地址不變,直接複用基類佈局。
class Base { int x, y, z; };
class Derived : Base { float f; char s[10]; };
// Derived 對象佈局:
// [Base::x][Base::y][Base::z][Derived::f][Derived::s[0]...s[9]]
// ↑
// Base* 指向此處即可正常訪問 x,y,z
struct Base { int x, y, z; };
struct Derived { struct Base base; float f; char s[10]; };
// 訪問基類成員
derived.base.x = 1; // 繁瑣
((struct Base*)&derived)->x = 1; // 強轉難看
#define BASE_MEMBERS \
int x; \
int y; \
int z
struct Base { BASE_MEMBERS; };
struct Derived { BASE_MEMBERS; float f; char s[10]; };
// 多層繼承
#define DERIVED_MEMBERS BASE_MEMBERS; float f; char s[10]
struct Derived { DERIVED_MEMBERS; };
#define MORE_MEMBERS DERIVED_MEMBERS; double d
struct More { MORE_MEMBERS; };
優點:derived.x = 1 直接訪問;維護時只改宏定義。缺點:宏濫用難調試,調試器顯示展開後代碼。
C 無成員函數概念,採用命名約定 + 顯式傳 this:
typedef struct Base Base;
typedef struct Derived Derived;
void Base_Display(Base *this) { printf("Base: %d\n", this->x); }
void Derived_Display(Derived *this) { printf("Derived: %d %f\n", this->x, this->f); }
// 子類調用父類方法
void Derived_Display(Derived *this) {
Base_Display((Base*)this); // 手動向上轉型
printf("f = %f\n", this->f);
}
核心:每個類持有一個方法表指針,指向該類的虛函數地址表。
/* 虛函數表定義 */
typedef struct Base_VTable {
void (*display)(Base*);
void (*destroy)(Base*);
} Base_VTable;
/* 基類結構 */
struct Base {
BASE_MEMBERS;
const Base_VTable *vptr;
};
/* 基類虛表實例(只读,全局唯一) */
static void Base_Display_impl(Base *t) { /* ... */ }
static void Base_Destroy_impl(Base *t) { /* ... */ }
const Base_VTable base_vtable = {
.display = Base_Display_impl,
.destroy = Base_Destroy_impl
};
/* 構造函數初始化 vptr */
Base* Base_Create(void) {
Base *b = malloc(sizeof(Base));
b->vptr = &base_vtable;
return b;
}
/* 多態調用 */
#define BASE_CALL(obj, method) (obj)->vptr->method(obj)
Base *b = Base_Create();
BASE_CALL(b, display); // 動態分發
派生類構造時:
| 特性 | 可行性 | 建議 |
|---|---|---|
| 單繼承數據 | ✅ 宏展開完美 | 推薦使用 |
| 多繼承/菱形繼承 | ⚠️ 極其複雜 | 放棄,改用 C++ |
| 虛函數/多態 | ✅ 手寫 vtable 可行 | 核心模塊可用 |
| 訪問控制 | ❌ 無法強制 | 靠命名規範(_private) |
| 構造/析構自動調用 | ❌ 無 RAII | 手動調用 Create/Destroy |
| 模板/泛型 | ❌ 僅靠宏模擬 | 簡單場景用宏,複雜上 C++ |
Type_Method)、文檔約定替代編譯器強制。