C語言如何實現繼承及容器
繼承的概念
繼承是面向對象軟體技術當中的一個概念,與多態、封裝共為面向對象的三個基本特徵。繼承可以使得子類具有父類的屬性和方法或者重新定義,追加屬性和方法。
面向對象中的重要概念就是類,在我們熟知的程式語言 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 語言實現的容器,我們可以管理內核對象,容器中包含的內核對象有對象本身的鏈表,拿執行緒打比方,我們新創建的執行緒也就可以通過鏈表的形式掛接到容器中對應的執行緒控制塊中,實現的效果如下:
最後,如果您覺的我的文章對您有所幫助,可以關注我的個人公眾號,期待與您一同前行~