繼承的概念
繼承是物件導向軟體技術當中的一個概念,與多型、封裝共為物件導向的三個基本特徵。繼承可以使得子類具有父類的屬性和方法或者重新定義,追加屬性和方法。
物件導向中的重要概念就是類,在我們熟知的程式語言 C++ 、Python 中都存在類的概念,通過現有的類從而繼承得到新的類。但是對於 C 語言來講,其中並不存在類的概念,那又如何實現繼承呢 ?
C 語言繼承的實現
筆者瞭解到 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 語言容器的實現
那 C 語言是如何建立一個容器呢 ?在 rt_thread 中,是通過一個全域性陣列的形式實現的,陣列的型別是 rt_object_information ,rt_object_information 的實現程式碼如下:
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 中容器中的物件有點多,筆者將其中物件進行縮減,擷取一部分出來,具體如下:
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 語言實現的容器,我們可以管理核心物件,容器中包含的核心物件有物件本身的連結串列,拿執行緒打比方,我們新建立的執行緒也就可以通過連結串列的形式掛接到容器中對應的執行緒控制塊中,實現的效果如下:
最後,如果您覺的我的文章對您有所幫助,可以關注我的個人公眾號,期待與您一同前行~