Skip to content

Latest commit

 

History

History
170 lines (125 loc) · 8.05 KB

1-内核对象模型.md

File metadata and controls

170 lines (125 loc) · 8.05 KB

RT-Thread 内核对象模型

在 RT-Thread 的内核对象模型是一种非常有趣的面向对象实现方式。面向对象由它非常优越的地方,我们应当取其精髓(即面向对象的思想,面向对象设计),也就是 RT-Thread 内核对象模型的来源。RT-Thread 实时操作系统中包含一个小型的,非常紧凑的对象系统,这个对象系统完全采用 C 语言实现。在了解 RT-Thread 内部或采用 RT-Thread 编程时有必要先熟悉他,它是 RT-Thread 实现的基础。

C语言的对象化模型

面向对象的特征主要包括:

  • 封装,隐藏内部实现
  • 继承,复用现有代码
  • 多态,改写对象行为

采用 C 语言实现的关键是如何运用 C 语言本身的特性来实现上述面向对象的特征。

封装

封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和使用者分开,使用者不必知晓行为实现的细节,只须用设计者提供的消息来访问该对象。这就像开车一样,并不需要知道车子的制造方法,只需要知道怎么操作即可,而我们也知道,汽车的有些核心技术对用户是保密的,这就是一种“屏蔽”。

在 C 语言中,大多数函数的命名方式是“动词名词”的形式,例如要获取一个 semaphore,会命名成 take_semaphore,重点在 take 这个动作上。在 RT-Thread 系统的面向对象编程中刚好相反,命名为 rt_sem_take,即“名词动词”的形式,重点在名词上,即这个对象,它体现了该对象的一种方法。另外对于某些方法,由于其仅局限在对象内部使用,因此可采用 static 关键词把它们的作用范围局限在一个文件的内部。通过这样的方式,就能把一些不想让用户知道的信息屏蔽在封装里,用户只能看到外层的接口,从而形成了面向对象的最基本的对象封装实现。

一般属于某个类的对象会有一个统一的创建,称之为构造过程。在 RT-Thread 中这些对象分为两类(以 semaphore 对象为例):

  • 对象内存数据块已经存在,需要对它进行初始化 – rt_sem_init;
  • 对象内存数据块还未分配,需要创建并初始化 – rt_sem_create.

可以这么认为,对象的创建(create)是以对象的初始化(init)为基础,相对来说创建动作多了一个内存分配的行为。

相对应的两类析构方式:

  • 由 rt_sem_init 初始化的 semaphore 对象 – rt_sem_detach;
  • 由 rt_sem_create 创建的 semaphore 对象 – rt_sem_delete.

继承

继承性是指子类自动共享父类的数据和方法的机制。它由类的派生功能体现。一个类直接继承类的其它全部描述,同时可修改和扩充。继承具有传递性,继承分为单继承(一个子类只有一父类)和多重继承(一个类有多个平行的父类,当前 RT-Thread 的对象系统不能支持)。类的对象是各自封闭的,如果没继承性机制,则类对象中数据、方法就会出现大量重复。继承不仅支持系统的可重用性,而且还促进系统的可扩充性。

类的实现代码如下:

/* 父类 */
struct parent_class
{
    int a, b;
    char *str;
};

/* 继承于父类的子类 */
struct child_class
{
    struct parent_class p;
    int a, b;
};

/* 操作示例函数*/
void func()
{
    struct child_class obj, *obj_ptr; /* 子类对象及指针 */
    struct parent_class *parent_ptr; /* 父类指针 */

    obj_ptr = &obj;
    /* 取父指针 */
①  parent_ptr = (struct parent_class*) &obj;

    /* 可通过转换过类型的父类指针访问相应的属性 */
    parent_ptr->a = 1;
    parent_ptr->b = 5;

    /* 子类属性的操作 */
    obj_ptr->a = 10;
    obj_ptr->b = 100;
}

在上面的例子中,注意 child_class 结构中第一个成员 p,这种声明方式代表 child_class 类型的数据中开始的位置包含一个 parent_class 类型的变量。在函数func中 obj 是一个 child_class 对象,正像这个结构类型指示的,它前面的数据应该包含一个 parent_class 类型的数据。在①处的强制类型赋值中 parent_ptr 指向了 obj 变量的首地址,也就是 obj 变量中的 p 对象。好了,现在 parent_ptr 指向的是一个真真实实的 parent 类型的结构,那么可以按照 parent 的方式访问其中的成员,当然也包括可以使用和 parent 结构相关的函数来处理内部数据,因为一个正常的,正确的代码,它是不会越界访问 parent 结构体以外的数据的。

经过这基本的结构体层层相套包含,对象简单的继存关系就体现出来了:父对象放于数据块的最前方,代码中可以通过强制类型转换获得父对象指针。

多态

对象根据所接收的消息而做出动作,同一消息为不同的对象接受时,对象所产生的动作完全有可能不一样,而这种现象就被称之为多态性。利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接受消息的对象自行决定,如此一来,同一消息即可调用不同的方法。例如 RT-Thread 系统中的设备:那些抽象设备就具备统一的读写接口,而串口虽然也是设备的一种,也应支持设备的读写,但串口的读写操作是串口所特有的,和其他设备的操作并非完全相同,因此对串口设备的操作应该和抽象设备区别开来,例如串口操作不应应用于 SD 卡设备中。

多态性的实现受到继承性的支持,利用类继承的层次关系,应把具有通用功能的协议存放在类层次中尽可能高的地方,而将实现这一功能的不同方法置于较低层次,这样,在这些低层次上生成的对象就能给通用消息以不同的响应。

在 RT-Thread 对象模型中,采用结构封装的指针形式可实现面向对象的多态效果,如下例所示:

#include <rtthread.h>

/* 抽象父类 */
struct parent_class
{ 
    int a;

    /* 反映不同类别属性的方法 */
    void (*vfunc)(struct parent_class* self, int a); 
};

/* 抽象类的方法调用 */
void parent_class_vfunc(struct parent_class *self, int a) 
{ 
    RT_ASSERT(self != RT_NULL); 
    RT_ASSERT(slef->vfunc != RT_NULL); 

    /* 调用对象本身的虚拟函数 */ 
    self->vfunc(self, a); 
}

/* 抽象类的虚拟函数 */
static void _parent_vfunc(struct parent_class* self, int a)
{
    RT_ASSERT(self != RT_NULL);
    self->a += a;

    /* 输出相应的信息 */
    rt_kprintf("parent's a=%d\n", self->a);
}

/* 抽象类的构造函数 */
void parent_class_init(struct parent_class* self)
{
    RT_ASSERT(self != RT_NULL);
    self->a = 1;
    self->vfunc = _parent_vfunc;
}

/* 继承自parent_class的子类 */ 
struct child_class 
{ 
    struct parent_class parent; 
    int b; 
}; 

/* 子类的虚拟函数实现 */
static void _child_class_vfunc(struct parent_class* self, int a)
{
    struct child_class *child = (struct child_class*)self;
    child->b = a + 10;

    /* 输出相应的信息 */
    rt_kprintf("child's b=%d\n", child->b);
}

/* 子类的构造函数 */ 
void child_class_init(struct child_class* self)
{ 
    struct parent_class* parent; 

    /* 强制类型转换获得父类指针 */ 
    parent = (struct parent_class*) self; 
    RT_ASSERT(parent != RT_NULL); 

    /* 设置子类的虚拟函数 */ 
    parent->vfunc = _child_class_vfunc; 
}

void obj_test()
{
    struct parent_class *ptr;
    struct parent_class parent;
    struct child_class child;

    parent_class_init(&parent);
    child_class_init(&child);

    ptr = (struct parent_class *)&parent;
    parent_class_vfunc(ptr, 10);
    ptr = (struct parent_class *)&child;
    parent_class_vfunc(ptr, 10);
}