完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
内核中的封装继承与多态
RT-Thread 虽然是使用面向过程的 C 语言来编写,但是处处都体现了面向对象的编程思想,先前对其感悟不够深,随着编写的程序越来愈多,对其理解也逐步加深。 封装 封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象者的使用分开,使用者不必知晓行为实现的细节,只须用设计者提供的消息来访问该对象。 在C语言中,大多数函数的命名方式是动词+名词的形式,例如要获取一个 semaphore,会命名成take_semaphore,重点在take这个动作上。在RT-Thread系统的面向对象编程中刚好相反,命名为 rt_sem_take,即名词+动词的形式,重点在名词上,体现了一个对象的方法。另外对于某些方法,仅局限在对象内部使用,它们将采用static修辞把作用范围局限在一个文件的内部。通过这样的方式,把一些不想让用户知道的信息屏蔽在封装里,用户只看到了外层的接口,从而形成了面向对象中的最基本的对象封装实现。 下面给出一段示例代码,展示C语言如何实现封装: // shape.h typedef struct{ int x; int y; }Shape; Shape * Shape_create(int x, int y); void Shape_init(Shape * self, int x, int y); void Shape_move(Shape * self, int dx, int dy); // shape.c Shape * Shape_create(int x, int y) { Shape * s = malloc(sizeof(Shape)); s->x = x; s->y = y; return s; } void Shape_init(Shape * self, int x, int y) { self->x = x; self->y = y; } void Shape_move(Shape * self, int dx, int dy) { self->x += dx; self->y += dy; } // main.c #include "shape.h" int main(int argc, char *argv[]) { Shape * s = Shape_create(0, 0); Shape_move(s, 10, 10); return 0; } 这里定义了一个叫做 Shape 的结构体,外界只能通过相关的函数来对这个 Shape 进行操作,例如创建(Shape_create), 初始化(Shape_init), 移动(Shape_move)等,不能直接访问 Shape 的内部数据结构。 如果想隐藏某个方法,即变成私有方法private,只需要在shape.c源文件中相应的方法前加上static限制该函数的作用范围为本文件内就可以了,既然隐藏了该方法也就不必在shape.h中声明该函数了。 虽然这里没有 class 这样的关键字,数据结构和相关操作是分开写的,看起来不太完美, 但确实是实现了封装。 继承 所有的内核对象都继承自更基础的对象,一直到最基础的对象 rt_object,下面从最上层的对象触发,看看这个继承过程是怎样的,以我最近编写的 uart16550 驱动为例。 struct zynq_uart16550 struct zynq_uart16550 { struct rt_serial_device serial; // 继承自 rt_serial_device XUartNs550 UartNs550Instance; struct rt_semaphore rx_sem; struct zynq_uart16550_config *config; }; struct rt_serial_device struct rt_serial_device { struct rt_device parent; // 继承自 rt_device const struct rt_uart_ops *ops; struct serial_configure config; void *serial_rx; void *serial_tx; }; struct rt_device struct rt_device { struct rt_object parent; // 继承自 rt_object enum rt_device_class_type type; /**< device type */ rt_uint16_t flag; /**< device flag */ rt_uint16_t open_flag; /**< device open flag */ rt_uint8_t ref_count; /**< reference count */ rt_uint8_t device_id; /**< 0 - 255 */ /* device call back */ rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size); rt_err_t (*tx_complete)(rt_device_t dev, void *buffer); #ifdef RT_USING_DEVICE_OPS const struct rt_device_ops *ops; #else /* common device interface */ rt_err_t (*init) (rt_device_t dev); rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag); rt_err_t (*close) (rt_device_t dev); rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size); rt_err_t (*control)(rt_device_t dev, int cmd, void *args); #endif #if defined(RT_USING_POSIX) const struct dfs_file_ops *fops; struct rt_wqueue wait_queue; #endif void *user_data; /**< device private data */ }; struct rt_object struct rt_object // 作为基础内核对象 { char name[RT_NAME_MAX]; /**< name of kernel object */ rt_uint8_t type; /**< type of kernel object */ rt_uint8_t flag; /**< flag of kernel object */ #ifdef RT_USING_MODULE void *module_id; /**< id of application module */ #endif rt_list_t list; /**< list node of kernel object */ }; 整个继承过程从后到前流程如下: struct zynq_uart16550 struct rt_serial_device struct rt_device struct rt_object 可以看出,每一次继承,更基础的内核类都作为更高级的内核类的第一个成员出现,这就像是每一次继承发生时,都为新产生的子类添加了更多功能,同时也继承了父类的属性。 多态—改写对象行为 在 RT-Thread 中如何体现了多态呢,这一点有时候会带给我困惑,但是如果仔细阅读平时编写的驱动程序的话,只要使用了 RT-Thread 的驱动框架,就会发现多态的实现几乎无处不在。 多态表示不同的对象可以执行相同的动作,但是要通过他们自己实现的代码来执行。什么意思呢,就是以设备举例,所有的 rt_device 都支持打开关闭,读写配置等操作,但是对不同类型设备这些操作的细节是不同的,例如串口设备的读写操作和块设备的读写操作不一样,但归根结底都是读写操作,在这一点上是没有区别的。 再举一个例子,同样是串口设备,这些串口设备都支持读写操作,但是读写 STM32 串口与操作 zynq 串口的细节就是不一样的,但归根结底,都是读写操作。也就是说,在这种情况下,有了父类,也就是串口设备,就知道说要将来可以进行读写操作,但是对于不同的子类来说,这些读写操作的具体细节要由子类来提供,将来真正要读写的时候,就调用子类提供的代码来实际操作。 下面以实际代码来说明多态在 RT-Thread 中的具体实现,首先观察如下代码: struct rt_device struct rt_device { struct rt_object parent; // 继承自 rt_object enum rt_device_class_type type; /**< device type */ rt_uint16_t flag; /**< device flag */ rt_uint16_t open_flag; /**< device open flag */ rt_uint8_t ref_count; /**< reference count */ rt_uint8_t device_id; /**< 0 - 255 */ /* device call back */ rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size); rt_err_t (*tx_complete)(rt_device_t dev, void *buffer); const struct rt_device_ops *ops; // 注意,这里向子类提供了 rt_device 类的操作表,子类就可以进行方法重写 // 使用这种方式实现了多态,使得继承自设备父类的子类可以用于操作不同类型的设备 // 省略多余代码... void *user_data; /**< device private data */ }; struct rt_serial_device struct rt_serial_device { struct rt_device parent; // serial device class 继承了父类 rt_device,也就拥有了父类的 rt_device_ops 函数表 const struct rt_uart_ops *ops; // 注意这里,这里向子类提供了 rt_uart_ops 类的操作表,子类就可以进行方法重写 struct serial_configure config; // 使用这种方式实现了多态,使得继承自串口父类的子类可以用于操作不同类型的串口设备 void *serial_rx; void *serial_tx; }; struct zynq_uart16550 struct zynq_uart16550 { struct rt_serial_device serial; // zynq_uart1655 设备类继承了父类 rt_serial_device,也就拥有了父类的 rt_uart_ops 函数表 XUartNs550 UartNs550Instance; struct rt_semaphore rx_sem; struct zynq_uart16550_config *config; }; struct rt_uart_ops struct rt_uart_ops // 支持某一特定类型串口设备所需要重写的方法 { rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg); rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg); int (*putc)(struct rt_serial_device *serial, char c); int (*getc)(struct rt_serial_device *serial); rt_size_t (*dma_transmit)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction); }; 具体实现实现添加操作函数时,实现如下: static const struct rt_uart_ops zynq_uart16550_ops = // 为 ops 函数表设置特定串口设备的操作函数 { .configure = zynq_uart16550_configure, .control = zynq_uart16550_control, .putc = zynq_uart16550_putc, .getc = zynq_uart16550_getc, }; 接下来使用驱动的初始化代码进行方法重写: uart16550_obj.serial.ops = &zynq_uart16550_ops; // 挂接 rt_serial_device 类中的 rt_uart_ops 函数表,相当于重写了函数表中的方法 |
|
相关推荐
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
971 浏览 0 评论
AI模型部署边缘设备的奇妙之旅:如何在边缘端部署OpenCV
3205 浏览 0 评论
tms320280021 adc采样波形,为什么adc采样频率上来波形就不好了?
1434 浏览 0 评论
2085 浏览 0 评论
1604 浏览 0 评论
75208 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 05:22 , Processed in 0.580688 second(s), Total 67, Slave 51 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号