继承:struct 嵌套复用公共字段
07 继承:struct 嵌套复用公共字段
C 语言 OOP 系列导航
本系列面向会 C 和单片机基础、刚进入企业项目的同学,从
struct封装一路讲到ops、多态和类 Linux 驱动框架。
封装能组织好单个对象。但项目里经常会出现“一类设备的多种实现”:GPIO LED、PWM LED、RGB LED、呼吸灯。
它们的点亮方式不同,但都有一些共同状态:引脚、亮度、是否点亮、是否初始化。
如果每种 LED 都独立定义结构体,公共字段会被复制很多次:
1 | typedef struct { |
一句话先懂:C 语言里的继承,就是把公共字段抽成一个 base struct,再把它嵌进具体结构体里。
先看问题:重复字段会让修改变成体力活
前四个字段在三个结构体里完全一样。以后新增一个公共字段,比如 name 或 error_code,就要改三处。公共检查逻辑也会重复。
| 问题 | 后果 |
|---|---|
| 公共字段复制 | 改字段要改多处 |
| 公共逻辑复制 | bug 修复容易漏 |
| 类型之间无关系 | 上层无法统一管理 |
| 新增 LED 类型 | 继续复制一份 |
这时需要的不是再封装一个函数,而是把公共部分抽出来。
抽出 LedBase_t
先定义基础结构体:
1 | typedef struct { |
具体类型把它作为成员嵌进去:
1 | typedef struct { |
关系可以这样理解:
| C 写法 | 设计含义 |
|---|---|
LedBase_t base; |
继承公共字段 |
gpio_led.base.pin |
访问父类字段 |
led_base_xxx(&obj->base) |
复用父类行为 |
| 具体结构体新增字段 | 派生类自己的数据 |
公共函数只操作 base
公共逻辑应该收敛到操作 LedBase_t * 的函数里:
1 | int led_base_init(LedBase_t *me, uint8_t pin) |
具体类型的初始化先复用基础逻辑,再补自己的字段:
1 | int pwm_led_init(PwmLed_t *me, uint8_t pin, uint8_t channel) |
公共部分只写一遍,具体差异留给具体类型。
base 通常放在第一个成员
工程上常把 base 放在结构体第一个成员:
1 | typedef struct { |
这样做有两个好处:
| 好处 | 说明 |
|---|---|
| 向上转型简单 | 可以把 &pwm_led.base 当成基础对象指针 |
| 后续反推方便 | container_of 能从 base 找回完整对象 |
示例:
1 | PwmLed_t pwm_led; |
这一步会在后面的多态与转型里继续展开。
继承解决公共部分,不解决行为差异
继承能复用公共字段和公共基础逻辑,但不能自动解决“不同类型怎么点亮”的问题。
| 类型 | 点亮方式 |
|---|---|
| GPIO LED | 写 GPIO 电平 |
| PWM LED | 设置 PWM 占空比 |
| RGB LED | 同时控制三个通道 |
| 呼吸灯 | 启动定时器渐变 |
如果 led_base_on() 写死为 gpio_write(),PWM LED 就不合适。行为差异要靠函数指针和 ops 操作表解决。
新人常见误区
| 误区 | 更准确的理解 |
|---|---|
| 继承就是为了少写字段 | 更重要的是建立“这些对象属于同一类”的关系 |
base 可以随便放位置 |
建议放第一个成员,后续转型更清楚 |
| 有了继承就能多态 | 继承只解决公共数据,多态还需要行为分发 |
| 所有相似结构都要抽 base | 只有公共字段和公共逻辑稳定时才抽 |
C/C++ 对照
C++ 风格:
1 | class LedBase { |
C 风格:
1 | typedef struct { |
| C++ | C |
|---|---|
class Base |
LedBase_t |
class Derived : public Base |
结构体内嵌 LedBase_t base |
| 父类字段 | base.xxx |
| 父类方法 | led_base_xxx(&obj->base) |
| 派生类字段 | 具体结构体自己的成员 |
嵌入式项目意义
嵌入式里经常有“抽象类型 + 多个具体实现”:
| 抽象类型 | 具体实现 |
|---|---|
| LED | GPIO LED、PWM LED、RGB LED |
| 存储 | Flash、EEPROM、SD 卡 |
| 通信 | UART、SPI、I2C |
| 传感器 | 温度、湿度、IMU |
用 base 抽出公共部分后,上层更容易统一管理,也为后面的多态打基础。
总结
C 语言的继承不是
extends,而是把公共字段和公共行为抽进base struct,再让具体对象把base嵌进去。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Rvosy的小破站!










