C语言中使用“结构体 + 函数指针”来模拟面向对象编程(OOP

C语言中使用"结构体 + 函数指针"来模拟面向对象编程(OOP)的"对象 + 方法"

是C语言中最经典、最常见的"伪OOP"写法之一。

这种写法在很多嵌入式系统、游戏引擎、图形库、驱动程序、GUI框架中都被广泛使用(GTK、libuv、SDL、Linux内核的部分模块等都大量使用类似手法)。

核心思想

把"数据"和"能作用在这份数据上的行为(函数)"打包放在一起。

c 复制代码
typedef struct {
    // 数据成员
    int x;
    int y;
    
    // 方法(函数指针)
    void (*move)(void* self, int dx, int dy);   // 第一个参数通常放自己
    void (*print)(const void* self);
    double (*distance_to_origin)(const void* self);
} Point;

最常见的几种写法对比(由简到复杂)

写法编号 风格 self 参数写法 推荐场景 易读性 维护性
1 显式传 self void (*func)(void* self, ...) 初学者、教学 ★★★☆☆ ★★★☆☆
2 用宏隐藏 self #define this self 个人项目、追求简洁 ★★★★☆ ★★☆☆☆
3 强类型 self(推荐) void (*func)(This* self, ...) 中大型项目、库设计 ★★★★★ ★★★★★
4 带继承(虚函数表) vtable + 结构体开头 需要多态的复杂系统 ★★★☆☆ ★★★★☆

下面我们用最推荐的第3种写法(强类型 self)来完整举例:

推荐写法示例(2024-2025年主流做法)

c 复制代码
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

// 前向声明
typedef struct Point Point;

// ==================== 1. 定义"类"(结构体) ====================
struct Point {
    // 数据部分
    float x;
    float y;

    // 方法表(函数指针)
    void    (*move)(Point* self, float dx, float dy);
    void    (*print)(const Point* self);
    float   (*distance_to)(const Point* self, const Point* other);
    void    (*destroy)(Point* self);   // 类似析构函数
};


// ==================== 2. 方法实现 ====================

static void point_move(Point* self, float dx, float dy) {
    self->x += dx;
    self->y += dy;
}

static void point_print(const Point* self) {
    printf("Point(%.2f, %.2f)\n", self->x, self->y);
}

static float point_distance_to(const Point* self, const Point* other) {
    float dx = self->x - other->x;
    float dy = self->y - other->y;
    return sqrtf(dx*dx + dy*dy);
}

static void point_destroy(Point* self) {
    // 如果有动态分配的成员,这里释放
    // 这里只是示例,实际可以 free(self) 如果是堆上创建的
    printf("Point at %p destroyed\n", (void*)self);
    // free(self);   // 如果是用 malloc 创建的才需要
}


// ==================== 3. 构造函数(工厂函数) ====================

Point* point_new(float x, float y) {
    Point* p = malloc(sizeof(Point));
    if (!p) return NULL;

    // 初始化数据
    p->x = x;
    p->y = y;

    // 绑定方法(最关键一步)
    p->move        = point_move;
    p->print       = point_print;
    p->distance_to = point_distance_to;
    p->destroy     = point_destroy;

    return p;
}


// ==================== 4. 使用方式 ====================

int main() {
    Point* p1 = point_new(3, 4);
    Point* p2 = point_new(0, 0);

    if (!p1 || !p2) {
        printf("内存分配失败\n");
        return 1;
    }

    p1->print(p1);                // Point(3.00, 4.00)
    p1->move(p1, 1, -2);
    p1->print(p1);                // Point(4.00, 2.00)

    printf("距离原点: %.2f\n", p1->distance_to(p1, p2));

    p1->destroy(p1);
    p2->destroy(p2);

    free(p1);
    free(p2);

    return 0;
}

更简洁但牺牲类型安全的写法(风格1)

很多人教学时会这样写(初学者常见):

c 复制代码
typedef struct {
    int x, y;
    void (*move)(void* self, int dx, int dy);
    void (*show)(void* self);
} Shape;

void shape_move(void* self, int dx, int dy) {
    Shape* s = (Shape*)self;
    s->x += dx;
    s->y += dy;
}

带"继承"与多态的进阶写法(简版)

c 复制代码
typedef struct {
    // 虚函数表必须放在最前面
    struct {
        void (*draw)(void* self);
        void (*move)(void* self, int dx, int dy);
    } vtable;

    int x, y;
    // ... 公共数据
} Shape;

typedef struct {
    Shape base;
    int radius;
} Circle;

void circle_draw(void* self) {
    Circle* c = self;
    printf("Circle at (%d,%d) r=%d\n", c->base.x, c->base.y, c->radius);
}

还有(简单版 / 推荐强类型版 / 带继承的多态版 / 其他变种)

相关推荐
三品吉他手会点灯2 小时前
C语言学习笔记 - 45.运算符和表达式 - 运算符3 - 逻辑运算符
c语言·笔记·学习
玖玥拾3 小时前
C/C++ 基础笔记(五)
c语言·c++·指针
QT-Neal3 小时前
C/C++ 程序段的概念与分类
c语言·c++
Luminous.4 小时前
C语言--day25
c语言·开发语言
luj_17684 小时前
硝酸核关联假说缺乏实验证据
c语言·开发语言·c++·经验分享·算法
无忧.芙桃4 小时前
数据结构之单链表
c语言·开发语言·数据结构
悠仁さん5 小时前
list 链式表基本功能模拟实现(双向有头指针循环链表)
c语言·数据结构·链表·list
三品吉他手会点灯5 小时前
C语言学习笔记 - 42.数据类型 - scanf函数深度解析
c语言·开发语言·笔记·学习
xxwxx__5 小时前
栈(Stack)详解:概念、实现与避坑指南
c语言·数据结构