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

C語言實現多態:用結構體與函數指針模擬虛函數表

     編輯:關於C語言

前言:為何在 C 語言中需要多態?

設計模式(如 Bridge、Proxy、Factory)的核心在於「抽象」。面向對象編程透過抽象類別建立框架骨架,再配合多態實現具體行為。C++ 原生支援繼承與虛函數,使這類設計極為自然;但純 C 語言缺乏語言級別的多態支援,許多開發者因此放棄此類設計思路。事實上,C 語言完全可以手動實現多態機制——關鍵在於理解虛函數表(vtable)的本質,並用結構體與函數指針加以模擬。

核心原理:VC 中的 Interface 本質

在 Visual C++ 中,__interface 關鍵字實際上被定義為 struct:

#ifndef Interface
#define Interface struct
#endif

當一個類別包含虛函數時,編譯器會在物件開頭植入一個隱藏指標 vptr,指向該類別的虛函數表(vtable)——一個由函數指標組成的陣列。C++ 只是在語言層面自動完成了這一切;在 C 中,我們只需手動構建相同的資料結構即可。

步驟一:定義「純虛結構」(介面)

使用結構體封裝一組函數指標,即可形成一個介面契約:

typedef struct {
    void (*Foo1)(void);
    char (*Foo2)(void);
    char* (*Foo3)(char* str);
} MyVirtualInterface;

步驟二:主類別持有介面指標

主類別 DoMyAct 不關心具體實現,僅持有介面指標並委派呼叫:

typedef struct {
    MyVirtualInterface* m_pInterface;
} DoMyAct;

void DoMyAct_SetInterface(DoMyAct* self, MyVirtualInterface* pInterface) {
    self->m_pInterface = pInterface;
}

void DoMyAct_Do(DoMyAct* self) {
    if (!self->m_pInterface) return;
    self->m_pInterface->Foo1();
    char c = self->m_pInterface->Foo2();
    // 進一步處理...
}

步驟三:具體實現類別(Act1、Act2)

每個具體類別內含一個介面陣列(可視為物件池),並提供建構函數初始化函數指標:

#define MAX_INSTANCES 10

typedef struct {
    MyVirtualInterface vtable[MAX_INSTANCES];
    int used[MAX_INSTANCES];
} Act1;

MyVirtualInterface* Act1_CreateInterface(Act1* self) {
    for (int i = 0; i < MAX_INSTANCES; ++i) {
        if (!self->used[i]) {
            self->used[i] = 1;
            self->vtable[i].Foo1 = Act1_Foo1_Impl;
            self->vtable[i].Foo2 = Act1_Foo2_Impl;
            self->vtable[i].Foo3 = Act1_Foo3_Impl;
            return &self->vtable[i];
        }
    }
    return NULL; // 池滿
}

// 具體實現函數
void Act1_Foo1_Impl(void) { /* ... */ }
char Act1_Foo2_Impl(void) { return 'A'; }
char* Act1_Foo3_Impl(char* str) { return str; }

Act2 結構與邏輯完全相同,僅實現函數不同。

步驟四:客戶端程式碼——消除 switch-case

主迴圈只需操作介面指標,完全解耦具體類型:

int main(void) {
    DoMyAct actor = {0};
    Act1 act1Pool = {0};
    Act2 act2Pool = {0};
    MyVirtualInterface* list[20];
    int count = 0;

    // 動態註冊實例
    MyVirtualInterface* p;
    if ((p = Act1_CreateInterface(&act1Pool))) list[count++] = p;
    if ((p = Act2_CreateInterface(&act2Pool))) list[count++] = p;

    // 統一調度
    for (int i = 0; i < count; ++i) {
        DoMyAct_SetInterface(&actor, list[i]);
        DoMyAct_Do(&actor); // 具體行為由介面決定
    }

    // 釋放資源...
    return 0;
}

總結

這種「結構體+函數指標」模式本質上是手工構建 vtable,實現了:

  • 介面隔離:主框架僅依賴抽象介面
  • 運行時多態:透過指標切換具體實現
  • 開閉原則:新增實現類別無須修改主框架

雖然 C 語言缺乏語法糖,但理解其底層機制後,同樣能寫出具備良好擴展性的框架式代碼。這不僅是技巧,更是對「組合優於繼承」原則的深度實踐。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved