首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

純 C 實現面向對象繼承機制:內存佈局、宏封裝、虛函數表手工構建

     編輯:關於C

繼承的內存佈局真相

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

C 模擬繼承的兩種方案

方案一:嵌入基類結構體(語法繁瑣)

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);  // 動態分發

派生類構造時:

  1. 調用基類構造(初始化基類成員 + 基類 vptr)。
  2. 覆蓋 vptr 指向派生類虛表(含覆寫方法與新增方法)。

工程權衡:做到何種程度?

特性可行性建議
單繼承數據✅ 宏展開完美推薦使用
多繼承/菱形繼承⚠️ 極其複雜放棄,改用 C++
虛函數/多態✅ 手寫 vtable 可行核心模塊可用
訪問控制❌ 無法強制靠命名規範(_private)
構造/析構自動調用❌ 無 RAII手動調用 Create/Destroy
模板/泛型❌ 僅靠宏模擬簡單場景用宏,複雜上 C++

最佳實踐原則

  1. 別過度模擬 C++:完全模擬還不如直接用 C++。
  2. 聚焦語義而非語法:命名規範(Type_Method)、文檔約定替代編譯器強制。
  3. 遵循 C 哲學:程式員掌控一切、直接簡明、零開銷抽象。
  4. 根據專案規模決定深度:小專案只用結構體+自由函數;核心框架引入 vtable 多態。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved