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

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

相关推荐
LDR0068 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
Luminous.8 天前
C语言--day30
c语言·开发语言
玖玥拾8 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
謓泽8 天前
C语言不是语法,是通往机器的地图。
c语言·开发语言
不会C语言的男孩8 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
2601_951643888 天前
C语言长文整理,关键字和数据类型
c语言·数据类型·关键字·嵌入式开发·格式化输出
m0_547486668 天前
《C#语言程序设计与实践》 全套PPT课件
c语言·c#·c语言程序设计
✎ ﹏梦醒͜ღ҉繁华落℘8 天前
编程基础 --高内聚,低耦合
c语言·单片机
QK_008 天前
C语言 static 关键字三大作用
c语言·开发语言
隔窗听雨眠8 天前
C语言函数递归从入门到精通(下):性能优化与工程实践
c语言·算法·性能优化