C語言雖無原生類、繼承、多態,但通過結構體封裝數據、函數指針封裝行為、不透明指針隱藏實現,可實現面向對象的核心優勢:封裝、接口隔離、多態(手工vtable)、模塊化。本文通過一個完整的鏈表 ADT (抽象數據類型) 實戰演示。
| OO概念 | C語言映射 |
|---|---|
| 類 | struct (數據) + 函數指針 (方法) |
| 對象 | 結構體實例 (含函數指針已綁定) |
| 封裝 | .h 暴露接口(函數指針類型),.c 隱藏結構體定義與實現 |
| 多態 | 不同實現綁定同一接口函數指針類型 (手工 vtable) |
| 構造/析構 | Create() 分配並綁定方法,Destroy() 釋放 |
| this 指針 | 對象指針作為第一個參數傳遞 (或結構體內嵌 _this 自引用) |
// list.h - 公開接口
#ifndef LIST_H
#define LIST_H
#ifdef __cplusplus
extern "C" {
#endif
// 數據載荷類型 (用戶自定義)
typedef struct Data {
int year;
char *name;
} Data;
// 不透明前向聲明 (隱藏實現細節)
typedef struct List List;
typedef struct Node Node;
// 構造函數
List *ListConstruction(void);
// 實例方法 (通過函數指針調用: list->insert(list, data))
void (*insert)(List *list, void *data);
void (*drop)(List *list, void *data);
void (*clear)(List **list);
int (*getSize)(List *list);
void* (*get)(List *list, int index);
void (*print)(List *list);
#ifdef __cplusplus
}
#endif
#endif
// list.c - 內部實現
#include "list.h"
#include
#include
#include
// 內部節點結構
struct Node {
void *data;
struct Node *next;
};
// 內部 List 結構 (包含函數指針 = vtable)
struct List {
struct List *_this; // 自引用,便於方法內訪問自身
struct Node *head; // 虛擬頭節點
int size;
// 方法指針
void (*insert)(struct List*, void*);
void (*drop)(struct List*, void*);
void (*clear)(struct List**);
int (*getSize)(struct List*);
void* (*get)(struct List*, int);
void (*print)(struct List*);
};
// 內部實現函數 (靜態,不暴露)
static void InsertImpl(struct List *list, void *data) { ... }
static void DropImpl(struct List *list, void *data) { ... }
static void ClearImpl(struct List **plist) { ... }
static int GetSizeImpl(struct List *list) { ... }
static void* GetImpl(struct List *list, int index) { ... }
static void PrintImpl(struct List *list) { ... }
// 構造函數:分配、初始化、綁定方法
List *ListConstruction(void) {
List *list = calloc(1, sizeof(List));
if (!list) return NULL;
list->head = calloc(1, sizeof(Node)); // 虛擬頭節點
if (!list->head) { free(list); return NULL; }
list->_this = list; // 自引用
list->size = 0;
// 綁定方法 (vtable 填充)
list->insert = InsertImpl;
list->drop = DropImpl;
list->clear = ClearImpl;
list->getSize = GetSizeImpl;
list->get = GetImpl;
list->print = PrintImpl;
printf("Construction successful\n");
return list;
}
// 內部實現細節略...
static void InsertImpl(struct List *list, void *data) {
if (!data || !list) return;
Node *node = malloc(sizeof(Node));
node->data = data; // 淺拷貝指針,所有權轉移
node->next = list->_this->head->next;
list->_this->head->next = node;
list->_this->size++;
printf("Insert successful\n");
}
static void DropImpl(struct List *list, void *data) {
if (!data || !list) return;
Data *target = (Data*)data;
Node *prev = list->_this->head;
Node *curr = prev->next;
while (curr) {
Data *d = (Data*)curr->data;
if (d->year == target->year && strcmp(d->name, target->name) == 0) {
prev->next = curr->next;
free(curr->data); // 釋放數據內存 (約定:列表擁有數據)
free(curr);
list->_this->size--;
printf("Drop successful\n");
return;
}
prev = curr;
curr = curr->next;
}
printf("Drop failure: not found\n");
}
static void PrintImpl(struct List *list) {
if (!list) return;
Node *saved_head = list->_this->head; // 保存頭節點
Node *curr = saved_head->next;
while (curr) {
Data *d = (Data*)curr->data;
printf("name=%s, year=%d\n", d->name, d->year);
curr = curr->next;
}
list->_this->head = saved_head; // 恢復 (雖然此處遍歷未修改head,但習慣保護)
}
// main.c
#include "list.h"
#include
#include
int main() {
List *list = ListConstruction();
if (!list) return 1;
// 插入數據 (列表獲取所有權,需 malloc)
Data *d1 = malloc(sizeof(Data)); d1->name = "xiaoming"; d1->year = 19;
list->insert(list, d1);
Data *d2 = malloc(sizeof(Data)); d2->name = "xiaoling"; d2->year = 29;
list->insert(list, d2);
Data *d3 = malloc(sizeof(Data)); d3->name = "JBV"; d3->year = 69;
list->insert(list, d3);
printf("Size: %d\n", list->getSize(list));
list->print(list);
// 刪除
Data key = {.name = "xiaoling", .year = 29};
list->drop(list, &key);
list->print(list);
// 獲取元素
Data *found = (Data*)list->get(list, 1);
if (found) printf("Index 1: %s, %d\n", found->name, found->year);
// 銷毀
list->clear(&list);
if (!list) printf("List cleared\n");
return 0;
}
Construction successful Insert successful Insert successful Insert successful Size: 3 name=JBV, year=69 name=xiaoling, year=29 name=xiaoming, year=19 Drop successful name=JBV, year=69 name=xiaoming, year=19 Index 1: xiaoming, 19 List cleared
// 定義統一的 vtable 結構
typedef struct ListVTable {
void (*insert)(void *self, void *data);
void (*drop)(void *self, void *data);
// ...
} ListVTable;
// 不同實現
static ListVTable array_list_vtable = { ArrayInsert, ArrayDrop, ... };
static ListVTable linked_list_vtable = { LinkedInsert, LinkedDrop, ... };
// 通用 List 結構
struct List {
ListVTable *vptr; // 指向具體 vtable
// 具體實現數據...
};
// 構造時綁定 vptr
List *CreateArrayList() { List *l = ...; l->vptr = &array_list_vtable; ... }
List *CreateLinkedList() { List *l = ...; l->vptr = &linked_list_vtable; ... }
// 調用: list->vptr->insert(list, data);
List*,不知內部結構,修改實現不影響調用者。Create() 中填充函數指針,實現「對象自帶方法」。_this:方法內通过 list->_this 訪問完整對象,模擬 this。malloc/free 數據 (示例中列表擁有數據,drop/clear 釋放)。vtable,支持多種實現 (數組/鏈表/跳表) 熱插拔。Module_Function 避免符號衝突,內部函數 static。C語言面向對象不是模仿 C++ 語法,而是運用 C 語言原生機制 (結構體、函數指針、模塊化、約定) 實現 OO 設計原則。這樣的代碼編譯快、運行效率高、無隱式開銷、易於靜態分析、適合嵌入式/系統級開發。掌握這套模式,C 語言大型項目架構將清晰、可維護、可擴展。