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

用 C 語言實現面向對象程式設計 (二):介面、多態與矩形/圓形類別實作

     編輯:關於C語言

承接上篇:OOSM 宏包已就緒,進入實戰建模

本篇展示如何用 OOSM 定義介面、實作具體類別、達成執行期多態。

介面定義:imeas.h — 測量周長與面積

#ifndef __IMEAS_H__
#define __IMEAS_H__
#include "oosm.h"

/* 介面:純行為契約,無狀態 */
interface(imeas) {
    double (*peri)(void* this);  // 周長
    double (*area)(void* this);  // 面積
};

#endif

關鍵設計:方法第一參數用 void*,允許任意實作類別的 this 隱式轉換(類似 C++ 虛函數 this 指標調整)。

矩形類別:crect.h / crect.c

/* crect.h */
#ifndef __CRECT_H__
#define __CRECT_H__
#include "imeas.h"

class(crect) {
    implements(imeas);  // 嵌入介面結構體
    double width;
    double height;
};
#endif
/* crect.c */
#include "crect.h"

static double peri(void* this) {
    crect* r = (crect*)this;
    return 2 * (r->width + r->height);
}

static double area(void* this) {
    crect* r = (crect*)this;
    return r->width * r->height;
}

constructor(crect) {
    mapping(imeas.peri, peri);
    mapping(imeas.area, area);
}

destructor(crect) {
    return 1;  // 釋放記憶體
}

圓形類別:ccirc.h / ccirc.c

/* ccirc.h */
#ifndef __CCIRC_H__
#define __CCIRC_H__
#include "imeas.h"

class(ccirc) {
    implements(imeas);
    double radius;
    double (*diam)(ccirc* this);  // 自有方法:直徑
};
#endif
/* ccirc.c */
#include "ccirc.h"
#define PI 3.1415926

static double peri(void* this) {
    ccirc* c = (ccirc*)this;
    return 2 * PI * c->radius;
}

static double area(void* this) {
    ccirc* c = (ccirc*)this;
    return PI * c->radius * c->radius;
}

static double diam(ccirc* this) {
    return 2 * this->radius;
}

constructor(ccirc) {
    mapping(imeas.peri, peri);
    mapping(imeas.area, area);
    mapping(diam, diam);  // 自有方法直接映射
}

destructor(ccirc) {
    return 1;
}

多態使用範例

#include 
#include "imeas.h"
#include "ccirc.h"
#include "crect.h"

int main() {
    ccirc* c = ccirc_new();
    crect* r = crect_new();
    
    c->radius = 9.23;
    r->width = 23.09; r->height = 78.54;
    
    /* 介面多態:統一呼叫介面方法 */
    printf("Circle:    直徑=%.2f 周長=%.2f 面積=%.2f\n", 
           c->diam(c), c->imeas.peri(c), c->imeas.area(c));
    printf("Rectangle: 周長=%.2f 面積=%.2f\n", 
           r->imeas.peri(r), r->imeas.area(r));
    
    ccirc_delete(c);
    crect_delete(r);
    return 0;
}

核心機制圖解

記憶體佈局 (crect 實例):
+------------------+
| imeas (介面)     |  ← 偏移量 0
|   peri: &peri    |
|   area: &area    |
+------------------+
| width: 23.09     |
| height: 78.54    |
+------------------+

記憶體佈局 (ccirc 實例):
+------------------+
| imeas (介面)     |  ← 偏移量 0 (關鍵!介面必須在首位)
|   peri: &peri    |
|   area: &area    |
+------------------+
| radius: 9.23     |
| diam: &diam      |
+------------------+

多態呼叫 c->imeas.peri(c):
1. c 指向 ccirc 實例開頭
2. c->imeas 取得介面子物件 (偏移量 0)
3. imeas.peri 取得函數指標 &ccirc_peri
4. 呼叫 ccirc_peri(c) → this 為 ccirc*,內部轉型存取 radius

介面與實作分離的優勢

  • 依賴倒置:高層模組依賴 imeas 介面,不依賴具體類別
  • 測試替身:單元測試可注入 Mock 實作
  • 插件架構:動態載入實作共享庫,僅需符合介面
  • 二進位相容:介面不變時,實作可獨立升級

注意事項與限制

  1. 介面必須為類別第一成員:保證 this 指標無需調整即可轉為介面指標
  2. 手動 this 傳遞:c->imeas.peri(c) 需顯式傳入,C 無隱式 this
  3. 單一介面繼承:implements 僅支援一個介面(可透過嵌套結構體擴展)
  4. 無建構子參數:_new 無參數,初始化需另行 init 方法

下一篇預告:繼承與正方形類別

將展示 extends(crect) 實現單一繼承,正方形作為矩形特化,覆寫周長/面積方法,構建完整類別體系。

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