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

C語言實現面向物件:手寫類、繼承、多態與虛函數表

     編輯:關於C語言

核心思想

C 無原生 OOP 支援,但可用結構體 + 函數指標模擬:

  • 封裝:結構體包含資料成員與方法指標。
  • 繼承:子類結構體首成員為父類結構體(記憶體佈局相容)。
  • 多態:父類方法指標指向子類實際實現(虛函數表機制)。

完整可編譯範例

#include <stdio.h>
#include <stdlib.h>

/* 基類 Shape */
typedef struct Shape {
    int edges;
    int (*getEdges)(struct Shape*);
    double (*getArea)(struct Shape*);
} Shape;

Shape *Shape_new() {
    Shape *s = malloc(sizeof(Shape));
    s->edges = 0;
    s->getEdges = Shape_getEdges;
    s->getArea = Shape_getArea;
    return s;
}
int Shape_getEdges(Shape *s) { return s->edges; }
double Shape_getArea(Shape *s) { return 0; }

/* 子類 Triangle */
typedef struct Triangle {
    Shape super;      // 必須為第一成員
    double base, height;
} Triangle;

Triangle *Triangle_new(double b, double h) {
    Triangle *t = malloc(sizeof(Triangle));
    t->super = *Shape_new();  // 繼承基類方法
    t->super.edges = 3;
    t->base = b; t->height = h;
    t->super.getEdges = Triangle_getEdges; // 覆寫
    t->super.getArea = Triangle_getArea;
    return t;
}
int Triangle_getEdges(Shape *s) { return ((Triangle*)s)->super.edges; }
double Triangle_getArea(Shape *s) {
    Triangle *t = (Triangle*)s;
    return t->base * t->height / 2;
}

/* 子類 Rectangle */
typedef struct Rectangle {
    Shape super;
    double width, height;
} Rectangle;

Rectangle *Rectangle_new(double w, double h) {
    Rectangle *r = malloc(sizeof(Rectangle));
    r->super = *Shape_new();
    r->super.edges = 4;
    r->width = w; r->height = h;
    r->super.getEdges = Rectangle_getEdges;
    r->super.getArea = Rectangle_getArea;
    return r;
}
int Rectangle_getEdges(Shape *s) { return ((Rectangle*)s)->super.edges; }
double Rectangle_getArea(Shape *s) {
    Rectangle *r = (Rectangle*)s;
    return r->width * r->height;
}

/* 多態演示 */
int main() {
    Shape *shapes[3];
    shapes[0] = (Shape*)Triangle_new(3, 4);
    shapes[1] = (Shape*)Rectangle_new(5, 6);
    shapes[2] = Shape_new();

    for (int i=0; i<3; i++) {
        printf("Edges: %d, Area: %.1f\n",
               shapes[i]->getEdges(shapes[i]),
               shapes[i]->getArea(shapes[i]));
    }
    // 輸出:
    // Edges: 3, Area: 6.0
    // Edges: 4, Area: 30.0
    // Edges: 0, Area: 0.0
    return 0;
}

關鍵技術點

  1. 記憶體佈局相容:子類首欄位為父類結構體,使 Shape* 可安全轉換為 Triangle*。
  2. 建構函數鏈:Triangle_new 先呼叫 Shape_new 初始化基類部分,再覆寫虛函數指標。
  3. 手動虛函數表:每個實例持有自己的方法指標(單繼承足夠),多繼承需引入獨立 vtable 指標。
  4. 解構函數:需手動實現 Shape_delete 遞歸釋放,C++ 編譯器自動處理此點。

進階:分離 vtable 節省記憶體

typedef struct Shape_vtable {
    int (*getEdges)(Shape*);
    double (*getArea)(Shape*);
} Shape_vtable;

static Shape_vtable shape_vtable = {Shape_getEdges, Shape_getArea};

typedef struct Shape {
    Shape_vtable *vptr;
    int edges;
} Shape;

// 子類建構時設定 vptr 指向各自的 static vtable
// 實例大小減少 (不再存每個方法指標),間接呼叫: obj->vptr->getArea(obj)

應用場景

  • 嵌入式系統無 C++ 編譯器、或需極致控制記憶體佈局。
  • GTK+/GObject、Linux 核心 file_operations、COM 介面皆採用此模式。
  • 理解此機制有助於深入掌握 C++ 物件模型與虛函數實現原理。

C 實現 OOP 是系統程式設計師的進階內功,手寫一套迷你物件模型能徹底消除對「類與虛函數」的魔法感,建立正確的記憶體與指標心智模型。

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