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

C 語言模擬面向對象:用函數指標實現封裝、繼承與多態

     編輯:關於C語言

為什麼 C 要模擬 OOP?

  • 嵌入式系統、內核開發、跨語言綁定 (Python C API, Lua C API) 常用 C。
  • 理解 OOP 底層機制:這樣才能真正掌握 C++ 虛表、繼承佈局、動態分派。
  • Linux 內核、GTK、Windows COM 都是 C 實現 OOP 的經典案例。

核心機制:結構體 + 函數指標


// 錯誤嘗試:C 結構體不能直接包含函數體
struct Test {
    void fun() { printf("hello"); } // 編譯錯誤!
};

// 正確:函數指標成員
struct Test {
    void (*Fun)(void); // 指向函數的指標
};

void real_fun(void) { printf("hello world\n"); }

int main() {
    struct Test t;
    t.Fun = real_fun;    // 綁定
    t.Fun();             // 調用
    return 0;
}

進階:帶「this」指標的方法調用


#include 

struct MyClass {
    char *name;
    int age;
    // 無參方法
    void (*funnull)(void);
    // 帶 this 指標的方法 (模擬 thiscall)
    void (*func)(struct MyClass *this);
};

// 實際函數定義
void real_funnull(void) { printf("hello world!\n"); }

void real_func(struct MyClass *this) {
    printf("MyClass's name is: %s\n", this->name);
    printf("MyClass's age is: %d\n", this->age);
}

int main() {
    // 構造對象:字面量初始化綁定方法
    struct MyClass mc = {
        "Simon", 25,
        real_funnull,
        real_func
    };

    mc.funnull();      // 靜態方法調用
    mc.func(&mc);      // 實例方法調用,手動傳 this
    return 0;
}

關鍵點:C++ 隱式傳遞 this,C 需顯式傳遞。這就是「非虛函數」的調用本質。

Linux 內核風格:運算表 (虛表雛形)


// 定義操作集合 (類似 vtable)
struct file_operations {
    int (*open)(struct inode *, struct file *);
    ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
    int (*release)(struct inode *, struct file *);
};

// 具體實現
static int my_open(struct inode *inode, struct file *filp) { ... }
static ssize_t my_read(struct file *filp, char __user *buf, size_t len, loff_t *off) { ... }

// 綁定 (靜態常量,唯讀段)
static const struct file_operations my_fops = {
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .release = my_release,
};

// 使用:文件系統層通過指標調用
filp->f_op->read(filp, buf, count, &pos);

這就是運行時多態:同一接口,不同實現,通過函數指標表分派。

模擬繼承:組合 + 指標轉換


struct Base {
    int base_data;
    void (*base_method)(struct Base *);
};

struct Derived {
    struct Base base; // 組合作為第一個成員
    int derived_data;
    void (*derived_method)(struct Derived *);
};

// 向上轉換安全:Derived* → Base* (地址相同)
void call_base(struct Base *b) { b->base_method(b); }

int main() {
    struct Derived d = { .base = { .base_data = 1 }, .derived_data = 2 };
    call_base((struct Base*)&d); // 正常工作
    return 0;
}

C++ 單繼承內存佈局正是如此:基類子對象在派生對象起始地址。

模擬虛函數:手動虛表指標


struct VTable_Shape {
    void (*draw)(struct Shape *);
    double (*area)(struct Shape *);
};

struct Shape {
    struct VTable_Shape *vptr; // 虛表指標
    // 通用數據...
};

// 具體類 Circle
struct Circle {
    struct Shape base;
    double radius;
};

void Circle_draw(struct Shape *s) { ... }
double Circle_area(struct Shape *s) { ... }

static struct VTable_Shape Circle_vtable = {
    .draw = Circle_draw,
    .area = Circle_area
};

void Circle_ctor(struct Circle *c, double r) {
    c->base.vptr = &Circle_vtable; // 綁定虛表
    c->radius = r;
}

// 多態調用
void draw_shape(struct Shape *s) { s->vptr->draw(s); }

這正是 C++ 編譯器為虛函數生成的代碼邏輯:vptr 指向類共享的 vtable,調用時間接跳轉。

對比總結:C vs C++ OOP 實現

特性C 手動實現C++ 原生支援
封裝不透明指標 + 內部結構體定義在 .cprivate/protected/public
繼承結構體組合 (基類作為首成員)class Derived : Base
多態手動虛表指標 + 顯式 this 傳遞虛函數、虛表、隱式 this
構造/析構手動 init()/destroy()構造函數/析構函數、RAII
類型安全靠程式員自律編譯器強制檢查
代碼量大量樣板代碼語言級語法糖

實戰建議

  1. 不透明指標封裝實現細節:.h 只聲明 struct Foo;,.c 定義完整結構,API 傳 Foo*。
  2. 統一錯誤碼:所有方法返回 int 錯誤碼,輸出參數用指標。
  3. 生命週期管理:提供 create()/destroy(),文檔明確所有權。
  4. 必要時才用虛表:大多數場景靜態綁定 (直接函數指標) 即可,性能更好。
  5. 參考成熟庫:GLib (GObject)、Linux 內核、Windows COM、libuv、nginx 核心模塊。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved