继承是面向对象软件技术当中的一个概念,与多态、封装共为面向对象的三个基本特征。继承可使得子类具备父类的属性和方法或者从新定义,追加属性和方法。
面向对象中的重要概念就是类,在咱们熟知的编程语言 C++ 、Python 中都存在类的概念,经过现有的类从而继承获得新的类。可是对于 C 语言来说,其中并不存在类的概念,那又如何实现继承呢 ?node
笔者了解到 C 语言实现继承是在阅读 rt-thread 源码中发现的,rt-thread 以小而美的物联网操做系统著称,在阅读其源码的时候,也可以感觉到其实现的精妙,其中对于内核对象的管理就是以面向对象的方式进行,采用结构体嵌套的方式实现了内核对象的继承与派生。在 rt-thread 的内核对象管理模块中,定义了通用的数据结构 rt_object ,笔者在这里姑且将其称之为父类,由于内核的线程对象,内存池对象,定时器对象,设备对象都是由 rt_object 派生而来。下面是 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 */ rt_list_t list; /**< list node of kernel object */ };
有了这个通用数据结构,咱们就能够依据此继承派生出新的内核对象,好比定时器对象,其实现细节以下所示:数组
struct rt_timer { struct rt_object parent; /**< inherit from rt_object */ rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL]; void (*timeout_func)(void *parameter); /**< timeout function */ void *parameter; /**< timeout function's parameter */ rt_tick_t init_tick; /**< timer timeout tick */ rt_tick_t timeout_tick; /**< timeout tick */ };
如上图代码所示,rt_timer 结构体内定义的 parent 就是由 rt_object 所继承下来的,在继承的基础上,又在结构体内增长了新的内容,从而造成了定时器对象。
所以对于 rt_thread 中的线程对象,内存池对象,定时器对象也能够用以下的一张图代表他们之间的关系。数据结构
上述就是关于继承的概念及 C 语言的具体的实现方式。编程语言
在 C++ 中对于容器的定义是这样的:在数据存储上,有一种对象类型,它能够持有其余对象或者指向其余对象的指针,这种对象类型就是容器,对于 C++ 来讲,有专门的构造函数实现容器,好比 vector() ,就能够建立一个容器。函数
那 C 语言是如何建立一个容器呢 ?在 rt_thread 中,是经过一个全局数组的形式实现的,数组的类型是 rt_object_information ,rt_object_information 的实现代码以下:ui
struct rt_object_information { enum rt_object_class_type type; /**< object class type */ rt_list_t object_list; /**< object list */ rt_size_t object_size; /**< object size */ };
其中,type 是用一个枚举类型实现的,具体实现以下:操作系统
enum rt_object_info_type { RT_Object_Info_Thread = 0, /**< The object is a thread. */ #ifdef RT_USING_SEMAPHORE RT_Object_Info_Semaphore, /**< The object is a semaphore. */ #endif #ifdef RT_USING_MUTEX RT_Object_Info_Mutex, /**< The object is a mutex. */ #endif RT_Object_Info_Unknown, /**< The object is unknown. */ };
对象的链表是基于这样实现的:线程
struct rt_list_node { struct rt_list_node *next; /**< point to next node. */ struct rt_list_node *prev; /**< point to prev node. */ };
因为 rt_thread 中容器中的对象有点多,笔者将其中对象进行缩减,截取一部分出来,具体以下:3d
static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] = { /* initialize object container - thread */ { RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread) }, #ifdef RT_USING_SEMAPHORE /* initialize object container - semaphore */ { RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore) }, #endif #ifdef RT_USING_MUTEX /* initialize object container - mutex */ { RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex) }, #endif }
上面就实现了一个容器,其中_OBJ_CONTAINER_LIST_INIT 是一个宏定义,具体定义以下:
#define _OBJ_CONTAINER_LIST_INIT(c) \ {&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}
其所用是初始化对象的链表,将头尾指针都指向自身,实现的效果以下:
因此整体来讲,rt_thread 中实现的容器里的内容就包含每个内核对象,而后内核对象是由一个结构体实现的,结构体包含着内核对象的类型,初始化好的内核对象链表以及内核对象的大小。既然如此咱们就能够对容器里的内容进行操做,好比得到指定内核对象的指针,代码以下:
rt_object_get_information(enum rt_object_class_type type) { int index; for (index = 0; index < RT_Object_Info_Unknown; index ++) if (rt_object_container[index].type == type) return &rt_object_container[index]; return RT_NULL; }
经过 C 语言实现的继承与派生,rt_thread 实现了多个内核对象的定义,而后经过 C 语言实现的容器,咱们能够管理内核对象,容器中包含的内核对象有对象自己的链表,拿线程打比方,咱们新建立的线程也就能够经过链表的形式挂接到容器中对应的线程控制块中,实现的效果以下:
最后,若是您觉的个人文章对您有所帮助,能够关注个人我的公众号,期待与您一同前行~