OOP C Programming

研究这部分内容的动机主要还是编写 STM32 的 C 代码的时候, 期望有一个固有的类能够管理其方法, 这样能够比较清晰的管理这些同属于一类的代码结构。 其实这里就涉及到了 面向对象 的编程思想, 将数据抽象为 方法, 类能表征数据的属性, 而方法则是数据属性及状态改变的一种行为。

之前在接触 Linux 驱动的时候其实就浅浅地接触到了 C 面向对象的思想, 例如 MTD 层抽象接口提供给 Block, 我们直接从顶层通过代码索引的方式是没办法找到具体的 Block 调用方法的。 我们只能看到 MTD 抽象出来的接口, 这一定程度上提供了数据的私有化的能力。

网上的相关文章众说纷纭, 但目前看来就是 C 现代编程 这本书讲的最清晰最明白, LW_OOPC 则是 高焕堂 以及其团队 MISOO 设计的方便 C 面向对象的宏定义模块, 能方便相关的开发。 其实这些文章读过来, 主要还是需要开发者用好 structMacropointer 这三类资源。 struct 可谓是重中之重, 可以看到在 C++ 中的 struct 和 class 仅在数据接口的私有性上存在差异, 而在 Rust 中则直接删除了 class 的定义, 转向了 struct + trait 的编程模式。

这篇文章不定期更新 OOP C 的一些想法, 其实读 C 现代编程 还是有很多疑惑的, 但是没用到就没太深刻的印象, 有问题再好好研究记录!

0. 资料汇总

1. 使用 struct 模块化编程

主要想法还是利用 struct 管理方法函数, 利用 static.c 文件中的函数进行私有化处理防止命名冲突。

另外, struct 也能起到虚函数的功能, 如下定义了一个 page_t 的结构体, 这个结构体规定了其内实现的方法函数的接口。

typedef struct {
    void (*painter)(void*);
    void (*handler)(void*);
} page_t;

2. 跨文件的全局变量

另外在整个工程中会需要跨文件使用相关的变量, 例如我在 encoder.h 定义了一个 struct Encoder 作为编码器类, 而我希望通过一个 g_encoder 变量作为跨文件的全局变量被不同文件使用。 例如在 encoder.c 中初始化方法函数, 而在其他文件中调用这些方法, 此时通过一个全局的变量就非常方便了。 这里需要区分 声明定义 这两个概念。

声明
向编译器说明一个变量或函数的信息,包括:名字、类型、初始值等,即声明变量、函数的属性细节, 包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。
定义
指明变量、函数存储在哪里,当定义发生时,系统为变量或函数分配内存单元。

我在这里是这样构想的, 对于特定功能的类, 例如 Encoder 编码器类, 则直接在 encoder.c 中定义一个这个类型的变量 g_encoder, 在 encoder.h 中则通过 extern 声明 g_encoder。 这样, 任何包含 encoder.h 的文件都能直接使用这个变量。 而一些影响设备属性的 primitive 类型的变量则通过 config.h 以及 config.c 进行声明和定义。 这样能够分离文件的职能, 不会导致 config.h 这个管理全局变量的文件不断 include 各种子设备而导致文件管理过于繁杂和臃肿。 但我需要在 config.h 用注释说明这些全局变量的位置以及功能, 方便后续进行查阅检索。

// encoder.c
Encoder g_encoder;
// encoder.c
typedef struct Encoder {
    ...
} Encoder;
extern Encoder g_encoder;

C语言:全局变量在多个c文件中公用的方法 - 奔流聚海 博客园