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

C 語言實現多態:結構體 + 函數指針模擬面向對象完整教程

     編輯:關於C

核心思想:手動構建虛函數表

C 無類/繼承,但可用結構體封裝數據、函數指針封裝行為、父結構體作為子結構體首字段實現單繼承多態。

一、基礎結構定義

/***** 父類:Shape *****/
typedef struct Shape {
    void (*print)(void);           // 虛函數
    float (*area)(struct Shape *); // 虛函數帶 this 指針
} Shape;

/***** 子類:Square *****/
typedef struct Square {
    Shape base;       // 必須是第一個成員!內存佈局兼容父類
    float width;
    float height;
} Square;

/***** 子類:Circle *****/
typedef struct Circle {
    Shape base;
    float radius;
} Circle;

/***** 子類:Triangle *****/
typedef struct Triangle {
    Shape base;
    float base_len;
    float height;
} Triangle;

二、具體實現與構造函數

/***** Square 實現 *****/
void square_print(void) { printf("Hello Square\n"); }
float square_area(Shape *this) {
    Square *sq = (Square *)this;  // 向下轉型
    return sq->width * sq->height;
}
void square_ctor(Square *sq, float w, float h) {
    sq->width = w; sq->height = h;
    sq->base.print = square_print;
    sq->base.area = square_area;
}

/***** Circle 實現 *****/
void circle_print(void) { printf("Hello Circle\n"); }
float circle_area(Shape *this) {
    Circle *c = (Circle *)this;
    return 3.14159f * c->radius * c->radius;
}
void circle_ctor(Circle *c, float r) {
    c->radius = r;
    c->base.print = circle_print;
    c->base.area = circle_area;
}

/***** Triangle 實現 *****/
void triangle_print(void) { printf("Hello Triangle\n"); }
float triangle_area(Shape *this) {
    Triangle *t = (Triangle *)this;
    return 0.5f * t->base_len * t->height;
}
void triangle_ctor(Triangle *t, float b, float h) {
    t->base_len = b; t->height = h;
    t->base.print = triangle_print;
    t->base.area = triangle_area;
}

三、多態調用演示

int main() {
    Square sq;   square_ctor(&sq, 3.0f, 4.0f);
    Circle ci;   circle_ctor(&ci, 2.5f);
    Triangle tr; triangle_ctor(&tr, 3.0f, 4.0f);

    Shape *shapes[] = { (Shape *)&sq, (Shape *)&ci, (Shape *)&tr };
    int n = 3;

    for (int i = 0; i < n; i++) {
        shapes[i]->print();                              // 動態分發
        printf("Area = %.2f\n\n", shapes[i]->area(shapes[i]));
    }
    return 0;
}

輸出

Hello Square
Area = 12.00

Hello Circle
Area = 19.63

Hello Triangle
Area = 6.00

四、內存佈局關鍵:為什麼 base 必須是第一個成員?

Square 內存:
+------------------+  <-- &sq (也是 &sq.base)
| Shape.base.print |  offset 0
| Shape.base.area  |  offset 8 (64位) / 4 (32位)
+------------------+
| width            |  offset 16/8
| height           |  offset 20/12
+------------------+

Shape* 指向 sq 時,取 offset 0,8 正好是 print/area 指針
若 base 不是首字段,指針偏移錯誤 → 崩潰

五、進階:模擬虛函數表 (vtable) 減少每對象開銷

/***** 共享 vtable *****/
typedef struct { void (*print)(void); float (*area)(Shape*); } ShapeVTable;

ShapeVTable square_vtable = { square_print, square_area };
ShapeVTable circle_vtable = { circle_print, circle_area };

typedef struct Shape2 { const ShapeVTable *vptr; } Shape2;
typedef struct Square2 { Shape2 base; float w, h; } Square2;

void square2_ctor(Square2 *s, float w, float h) {
    s->w = w; s->h = h;
    s->base.vptr = &square_vtable;
}

/* 調用 */
((Shape2 *)&sq2)->vptr->print();
((Shape2 *)&sq2)->vptr->area((Shape2 *)&sq2);

優點:每對象只存一個 vptr,方法不重複;缺點:構造時需手動綁定 vptr。

六、工程應用場景

  • 嵌入式狀態機:每個狀態一個結構體,enter/exit/handle 為虛函數
  • 驅動程序框架:file_operations、block_device_operations (Linux 內核)
  • 插件架構:主程序定義接口結構體,插件實現並導出構造函數
  • GUI 控件樹:Widget 基類,Button/Label/Window 子類

七、總結

C 實現多態三板斧:父結構體首字段嵌入、函數指針作為虛函數、構造函數綁定具體實現。理解內存佈局與指針轉型,即可在 C 中構建靈活、高性能的面向對象框架。

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