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);
}
还有(简单版 / 推荐强类型版 / 带继承的多态版 / 其他变种)