11 接口设计:虚函数不实现会怎样
11 接口设计:虚函数不实现会怎样
C 语言 OOP 系列导航
有了 ops 表,就有了 C 语言版虚函数。但如果某个函数指针是 NULL,会怎样?答案很直接:不检查就调用,程序直接崩。
ops 槽位为空
假设有人偷懒把 deinit 写成 NULL:
1 | static const LedOps_t pwm_led_ops = { |
如果 dispatch 直接 return me->ops->deinit(me);,就是空函数指针调用 — 比普通返回错误更危险。
接口合同:先定义清楚哪些必须实现
| 函数 | 是否必须 | 说明 |
|---|---|---|
on |
必须 | LED 至少要能点亮 |
off |
必须 | LED 至少要能关闭 |
set_brightness |
可选 | 普通 GPIO LED 可能不支持真实调光 |
deinit |
可选或必须 | 看框架是否负责资源释放 |
这个合同要体现在代码里。初始化时检查必选函数:
1 | static bool led_ops_is_valid(const LedOps_t *ops) |
默认实现:简单设备不用重复写
普通 GPIO LED 不支持真实亮度调节,但可以给一个默认实现:
1 | static int led_default_set_brightness(LedBase_t *me, uint8_t brightness) |
dispatch 里先查具体实现,没有就走默认:
1 | int led_set_brightness(LedBase_t *me, uint8_t brightness) |
简单设备不用重复写一堆模板函数。
C 语言 vs C++ 的接口机制
| 概念 | C++ | C 语言 |
|---|---|---|
| 纯虚函数 | virtual void on() = 0 |
ops 槽位必须非 NULL |
| 虚函数 | virtual void on() |
ops 槽位可被覆盖 |
| 默认实现 | 基类提供实现 | dispatch 里走默认函数 |
| 接口合同 | 抽象类声明 | ops_is_valid() 检查 |
C 语言没有编译器帮你检查纯虚函数是否实现,所以你要自己写检查。
Linux 风格接口为什么常有 NULL
很多大型 C 框架把 ops 设计成”部分可选”。比如不是每个设备都支持 ioctl,所以调用前必须检查:
1 | int device_ioctl(Device_t *dev, int cmd, void *arg) |
关键是:文档和代码都要说明哪些函数可选。
驱动接口不是堆函数指针
在设计 ops 时,必须想清楚:
| 设计问题 | 应该明确 |
|---|---|
| 哪些操作必须支持 | 初始化时检查 |
| 哪些操作可以不支持 | dispatch 返回错误或默认实现 |
| 不支持时返回什么 | 统一错误码 |
| 默认行为是什么 | 写成明确函数 |
| 谁负责释放资源 | deinit 合同 |
否则 ops 会从”解耦工具”变成”空指针雷区”。
ops是接口合同,不是函数指针杂货铺。必选函数初始化时检查,可选函数调用前判断,能默认实现就集中默认实现。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Rvosy的小破站!








