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

C語言實現面向對象編程:結構體+函數指針封裝鏈表ADT完整實戰

     編輯:關於C語言

C語言實現面向對象編程:結構體+函數指針封裝鏈表ADT完整實戰

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;
}

// 內部實現細節略...

四、關鍵實現細節

1. 插入 (頭插法)


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");
}

2. 刪除 (按數據內容匹配)


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");
}

3. 遍歷打印 (保護頭節點)


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);

八、總結:C語言 OOP 最佳實踐清單

  1. 接口與實現分離:.h 只有前向聲明和函數指針類型,.c 定義結構體和靜態實現函數。
  2. 不透明指針:用戶只見 List*,不知內部結構,修改實現不影響調用者。
  3. 構造函數綁定方法:Create() 中填充函數指針,實現「對象自帶方法」。
  4. 自引用 _this:方法內通过 list->_this 訪問完整對象,模擬 this。
  5. 所有權約定:明確誰負責 malloc/free 數據 (示例中列表擁有數據,drop/clear 釋放)。
  6. 虛擬頭節點:統一插入/刪除邊界條件,代碼更簡潔。
  7. 多態擴展:抽離 vtable,支持多種實現 (數組/鏈表/跳表) 熱插拔。
  8. 命名規範:Module_Function 避免符號衝突,內部函數 static。

C語言面向對象不是模仿 C++ 語法,而是運用 C 語言原生機制 (結構體、函數指針、模塊化、約定) 實現 OO 設計原則。這樣的代碼編譯快、運行效率高、無隱式開銷、易於靜態分析、適合嵌入式/系統級開發。掌握這套模式,C 語言大型項目架構將清晰、可維護、可擴展。

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