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

C 語言模擬面向對象:函數指針實現 List 接口與鏈表完整實戰

     編輯:關於C語言

C 語言模擬面向對象:函數指針實現 List 接口與鏈表完整實戰

C 語言雖無原生類與繼承,但通過 **結構體 + 函數指針** 可完美模擬「接口 + 實現 + 封裝」的 OO 模式。本文以泛型鏈表 List 為例,完整展示接口定義、構造/析構、方法綁定、_this 指針模擬 this、測試驗證全流程。

核心設計模式

OO 概念C 語言映射
接口結構體包含函數指針成員
類結構體類型定義
對象/實例結構體變量 (堆分配)
this 指針結構體內 _this 自引用指針
方法接收 _this 為隱式參數的函數
封裝靜態全局變量 + 只暴露接口頭文件
泛型void * 數據域 + 調用者自行類型轉換

接口定義 IList.h


#ifndef _ILIST_H
#define _ILIST_H

typedef struct Node {
    void *data;
    struct Node *next;
} Node;

typedef struct List {
    struct List *_this;          // 模擬 this 指針
    Node *head;                  // 虛擬頭節點
    int size;                    // 元素計數
    
    // 方法指針 (接口)
    void (*insert)(void *node);
    void (*drop)(void *node);
    void (*clear)();
    int (*getSize)();
    void* (*get)(int index);
    void (*print)();
} List;

// 構造/析構函數聲明
List *ListConstruction();
void ListDeconstruction();

#endif

實現文件 List.c 關鍵片段


#include 
#include 
#include "IList.h"

static Node *node = NULL;  // 文件級靜態,模擬私有成員
static List *list = NULL;  // 單例模式 (演示用),實際應支持多實例

// 內部輔助函數聲明
static void insert(void *node);
static void drop(void *node);
static void clear();
static int getSize();
static void* get(int index);
static void print();

// 構造函數:分配內存、初始化字段、綁定方法指針、設置 _this
List *ListConstruction() {
    list = (List*)malloc(sizeof(List));
    node = (Node*)malloc(sizeof(Node)); // 虛擬頭節點
    list->head = node;
    list->size = 0;
    list->_this = list;  // 關鍵:自我引用
    
    // 綁定方法
    list->insert = insert;
    list->drop = drop;
    list->clear = clear;
    list->getSize = getSize;
    list->get = get;
    list->print = print;
    
    return list;
}

// 插入:頭插法 (虛擬頭節點後)
void insert(void *data) {
    Node *cur = (Node*)malloc(sizeof(Node));
    cur->data = data;
    cur->next = list->_this->head->next;
    list->_this->head->next = cur;
    (list->_this->size)++;
}

// 刪除:按數據指釖比較查找並刪除
void drop(void *data) {
    Node *prev = list->_this->head;
    Node *curr = prev->next;
    while (curr) {
        if (curr->data == data) { // 指針比較,調用者確保同一對象
            prev->next = curr->next;
            free(curr);
            (list->_this->size)--;
            break;
        }
        prev = curr;
        curr = curr->next;
    }
}

// 獲取指定索引元素 (0-based)
void* get(int index) {
    if (index < 0 || index >= list->_this->size) return NULL;
    Node *cur = list->_this->head->next;
    for (int i = 0; i < index; ++i) cur = cur->next;
    return cur->data;
}

// 清空並釋放所有節點
void clear() {
    Node *cur = list->_this->head->next;
    while (cur) {
        Node *next = cur->next;
        free(cur);
        cur = next;
    }
    list->_this->head->next = NULL;
    list->_this->size = 0;
    // 注意:list 結構體自身不在此釋放,由 ListDeconstruction 處理
}

void print() {
    Node *cur = list->_this->head->next;
    int i = 0;
    while (cur) {
        printf("[%p] = {%s}\n", (void*)&cur->data, (char*)cur->data);
        cur = cur->next;
    }
}

測試驗證 Main.c


#include 
#include "IList.h"

int main() {
    List *list = ListConstruction();
    
    // 插入字符串字面量 (存儲在只讀區,生命周期全程有效)
    list->insert("Apple");
    list->insert("Borland");
    list->insert("Cisco");
    list->insert("Dell");
    list->insert("Electrolux");
    list->insert("FireFox");
    list->insert("Google");
    
    printf("初始列表:\n");
    list->print();
    printf("Size: %d\n\n", list->getSize());
    
    // 刪除 "Electrolux" 和 "Cisco"
    // 注意:需傳遞與插入時完全相同的指針值
    Node node;
    node.data = "Electrolux"; node.next = NULL;
    list->drop(&node);
    node.data = "Cisco"; node.next = NULL;
    list->drop(&node);
    
    printf("刪除後:\n");
    list->print();
    printf("Size: %d\n", list->getSize());
    
    list->clear();
    free(list); // 釋放 List 結構體
    return 0;
}

運行結果

初始列表:
[00489760] = {Google}
[00489730] = {FireFox}
[00489700] = {Electrolux}
[004896D0] = {Dell}
[004896A0] = {Cisco}
[00489670] = {Borland}
[00489640] = {Apple}
Size: 7

刪除後:
[00489760] = {Google}
[00489730] = {FireFox}
[004896D0] = {Dell}
[00489670] = {Borland}
[00489640] = {Apple}
Size: 5

關鍵技術點深度解析

  1. _this 自引用指針:方法內部通過 list->_this->head 訪問對象自身數據,模擬 C++ this->head。構造時必須 list->_this = list。
  2. 靜態全局變量封裝:static List *list 對外不可見,只能通過接口函數訪問,實現信息隱藏。
  3. 泛型 void *:數據域類型擦除,調用者負責類型安全 (如 (char*)list->get(i))。
  4. 函數指針綁定:構造時將具體函數地址賦給結構體函數指針成員,實現動態分發。
  5. 單例 vs 多實例:示例為簡化用單例,生產代碼應移除靜態 list,構造函數返回新分配對象,方法接收 List *self 參數。

擴展方向:線程池雛形

將上文 Executor (函數指針封裝任務) 放入此 List,添加同步機制 (互斥鎖/條件變量) 即可構建簡易線程池:

  • List 作任務隊列
  • 工作線程循環 drop 取任務執行
  • insert 提交新任務

C 語言「結構體 + 函數指針 + _this 自引用」三板斧,打通了面向對象設計思想在過程式語言中的落地路徑,是理解 C++ 編譯器底層實現 (vtable、this 指針調整) 的最佳切入點。

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