封装

隐藏内部数据,只暴露接口函数。

"头文件"暴露的内容

point.h

typedef struct Point point;  // 前向声明,不暴露成员

/ 公开的接口函数
Point* point_new(const char* name, int x, int y);
void point_del(Point* p);

// 只能通过函数访问数据,外部无法直接读写 p->x
void point_set_x(Point* p, int x);
int point_get_x(const Point* p);

void point_set_y(Point* p, int y);
int point_get_y(const Point* p);
//方法(函数)
void point_print(const Point* p);

"实现" —— 结构体完整定义对外不可见

point.c

struct Point {
    char   owner[32];/* 外部无法直接访问 */
    int    x;        /* 完全隐藏 */
    int    y;  		 /* 对外不可见 */
};

函数实现

point.c

// 构造函数
Point* point_new(const char* name, int x, int y) {
    Point* p = malloc(sizeof(Point));
    if (p) {
        strncpy(p->name, name, sizeof(p->name) - 1);
        p->name[sizeof(p->name) - 1] = '\0';
        p->x = x;
        p->y = y;
    }
    return p;
}

// 析构函数
void point_del(Point* p) {
    free(p);
}

// 封装后的 Setter / Getter(带合法性检查)
void point_set_x(Point* p, int x) {
    if (p) p->x = x;  // 可以在这里加范围限制,如 if(x < 0) return;
}

int point_get_x(const Point* p) {
    return p ? p->x : 0;
}

void point_set_y(Point* p, int y) {
    if (p) p->y = y;
}

int point_get_y(const Point* p) {
    return p ? p->y : 0;
}

void point_print(const Point* p) {
    if (p) {
        printf("Point(%s, %d, %d)\n", p->name, p->x, p->y);
    }
}

使用者视角

#include "point.h"

int main() {
    // Point p1;  // 这行会编译报错!因为外部不知道 Point 的大小,无法在栈上分配
    Point* p = point_new("origin", 0, 0); // 只能通过堆分配

    point_set_x(p, 100);
    // p->x = 200;  // 编译报错!提示:dereferencing pointer to incomplete type(不完整类型)

    point_print(p);
    point_del(p);
    return 0;
}

C 的封装靠"文件作用域 + 不透明指针"实现,而非 private 关键字,比如私有方法用static修饰(文件作用域),再比如typedef struct Point point;修饰的结构体其他地方通过构建函数拿到返回的指针是不能直接拿里面的变量的,因为其他地方都不知道这个Point 有什么变量和方法(不透明指针)