C 語言如何實現繼承與容器

2021-02-19 wenzi嵌入式軟體

筆者能力有限,寫公眾號的目的主要是為了積累,同時也能夠激勵自己養成積累的習慣。如果文中有不對的地方,還請各位朋友能及時地給我指出來,我將不勝感激,謝謝~

繼承的概念

繼承是面向對象軟體技術當中的一個概念,與多態、封裝共為面向對象的三個基本特徵。繼承可以使得子類具有父類的屬性和方法或者重新定義,追加屬性和方法。面向對象中的重要概念就是類,在我們熟知的程式語言 C++ 、Python 中都存在類的概念,通過現有的類從而繼承得到新的類。但是對於 C 語言來講,其中並不存在類的概念,那又如何實現繼承呢 ?

C 語言繼承的實現

筆者了解到 C 語言實現繼承是在閱讀 rt-thread 源碼中發現的,rt-thread 以小而美的物聯網作業系統著稱,在閱讀其源碼的時候,也能夠感受到其實現的精妙,其中對於內核對象的管理就是以面向對象的方式進行,採用結構體嵌套的方式實現了內核對象的繼承與派生。在 rt-thread 的內核對象管理模塊中,定義了通用的數據結構 rtobject ,筆者在這裡姑且將其稱之為父類,因為內核的線程對象,內存池對象,定時器對象,設備對象都是由 rtobject 派生而來。下面是 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 */

};

如上圖代碼所示,rttimer 結構體內定義的 parent 就是由 rtobject 所繼承下來的,在繼承的基礎上,又在結構體內增加了新的內容,從而形成了定時器對象。因此對於 rt_thread 中的線程對象,內存池對象,定時器對象也可以用如下的一張圖表明他們之間的關係。

上述就是關於繼承的概念及 C 語言的具體的實現方式。

容器的概念

在 C++ 中對於容器的定義是這樣的:在數據存儲上,有一種對象類型,它可以持有其他對象或者指向其他對象的指針,這種對象類型就是容器,對於 C++ 來說,有專門的構造函數實現容器,比如 vector() ,就可以創建一個容器。那 C 語言是如何創建一個容器呢 ?在 rtthread 中,是通過一個全局數組的形式實現的,數組的類型是 rtobjectinformation ,rtobject_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

}

上面就實現了一個容器,其中OBJCONTAINERLISTINIT 是一個宏定義,具體定義如下:

#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 語言實現的容器,我們可以管理內核對象,容器中包含的內核對象有對象本身的鍊表,拿線程打比方,我們新創建的線程也就可以通過鍊表的形式掛接到容器中對應的線程控制塊中,實現的效果如下: 

您的建議是對我的最大地提升,您的在看是對我最大的鼓勵

相關焦點

  • C語言實現繼承和多態
    下面就用純C自己寫一個簡單的類的繼承、多態。繼承 繼承大體是用派生類的第一個地址放置基類的對象,這樣我們用強轉也不會出現問題。print_child(struct _Parent *)} } c=4 } Child *ptr->print(ptr);//ptr0x013848c8 {a=1 b=2 print=0x003d65d4 {Test.exe!
  • C語言陷阱與技巧第29節,很多程式設計師不知道,C語言也能「繼承」父類
    之前兩節探討了C語言進行「面向對象」編程的可能性。現在已經了解,結合C語言的指針和結構體語法,基本能夠實現對象語法最核心的部分,即成員函數和成員變量。另外,上一節討論了如何利用指針,將公開的成員變量,封裝成 private(私有)變量,由此也可以看出C語言指針語法的強大。
  • C 語言面向對象編程 - 繼承
    語言面向對象編程 – 封裝的簡單概念和實現。上一篇文章的具體內容,可以查看以下連結: C 語言面向對象編程 - 封裝本篇文章繼續來討論一下,如何使用 C 語言實現面向對象編程的一個重要特性:繼承。繼承就是基於一個已有的類(一般稱作父類或基類),再去重新聲明或創建一個新的類,這個類可以稱為子類或派生類。子類或派生類可以訪問父類的數據和函數,然後子類裡面又添加了自己的屬性和數據。
  • 如何用C語言實現面向對象編程OOP?
    如何用C語言實現面向對象編程OOP? C語言的特性,實現OOPC是一門面向過程的語言,但它依舊可以實現大多數面向對象所能完成的工作。比如面向對象的三大特性:封裝、繼承、多態。我們以下圖來寫代碼舉例子。
  • C語言實現面向對象的原理
    面向對象編程,也就是大家說的OOP(Object Oriented Programming)並不是一種特定的語言或者工具,它只是一種設計方法、設計思想,它表現出來的三個最基本的特性就是封裝、繼承與多態。閱讀文本之前肯定有讀者會問這樣的問題:我們有C++面向對象的語言,為什麼還要用C語言實現面向對象呢?
  • 如何用C語言實現OOP
    很多語言例如:C++和Java等都是面向對象的程式語言,而我們通常說C是面向過程的語言,那麼是否可以用C實現簡單的面向對象呢?答案是肯定的!C有一種數據結構叫做結構體(struct)和函數指針,使用結構體和函數指針便可實現面向對象的三大特性。
  • 面向對象編程OOP的C語言實現
    解釋區分一下C語言和OOP我們經常說C語言是面向過程的,而C++是面向對象的,然而何為面向對象,什麼又是面向過程呢?不管怎麼樣,我們最原始的目標只有一個就是實現我們所需要的功能,從這一點說它們是殊途同歸的。過程與對象只是側重點不同而已。
  • 真的可以用C語言實現面向對象編程OOP
    解釋區分一下C語言和OOP 我們經常說C語言是面向過程的,而C++是面向對象的,然而何為面向對象,什麼又是面向過程呢?不管怎麼樣,我們最原始的目標只有一個就是實現我們所需要的功能,從這一點說它們是殊途同歸的。
  • 一步步分析:C語言如何面向對象編程
    這篇文章,我們就來聊聊如何在C語言中利用面向對象的思想來編程。也許你在項目中用不到,但是也強烈建議你看一下,因為我之前在跳槽的時候就兩次被問到這個問題。第二個問題:繼承繼承描述的是對象之間的關係,子類通過繼承父類,自動擁有父類中的屬性和行為(也就是方法)。這個問題只要理解了C語言的內存模型,也不是問題,只要在子類結構體中的第一個成員變量的位置放置一個父類結構體變量,那么子類對象就繼承了父類中的屬性。另外補充一點:學習任何一種語言,一定要理解內存模型!
  • C語言對象編程第二彈:繼承
    以實例分享了C語言對象編程的繼承與抽象。 本次分享C語言對象編程第二彈:繼承。 繼承簡單說來就是父親有的東西,孩子可以繼承過來。 當創建一個類時,我們不需要重新編寫新的數據成員和成員函數,只需指定新建的類繼承了一個已有的類的成員即可。
  • 真的可以,用C語言實現面向對象編程OOP
    解釋區分一下C語言和OOP我們經常說C語言是面向過程的,而C++是面向對象的,然而何為面向對象,什麼又是面向過程呢?不管怎麼樣,我們最原始的目標只有一個就是實現我們所需要的功能,從這一點說它們是殊途同歸的。過程與對象只是側重點不同而已。
  • 網易有道CEO周楓:Go語言繼承了C語言的靈活簡單
    他認為Go很好地繼承了C語言靈活、簡單有效的思想;Go有很高的生產效率;Go精選了一些複雜事情的優秀解決辦法,通過語言功能和標準庫提供出來。Go語言作為一個新的程式語言(8年歷史,對比C的40年,Java的20年很年輕了),很多人都挺喜歡,也寫了不少推薦,網際網路公司中在生產系統中使用Go的(主要是在雲端服務中)也在逐漸增多。
  • C中的繼承和多態
    來自:吳秦 - 博客園作者:吳秦連結:http://www.cnblogs.com/skynet/archive/2010/09/23/1833217.html1、引言繼承和多態是面向對象語言最強大的功能
  • C語言如何實現動態擴容的string
    作者丨程序喵大人來源丨程序喵大人最近工作中使用C
  • C語言中的面向對象(1)-類模擬和多態,繼承
    正在閱讀:C語言中的面向對象(1)-類模擬和多態,繼承C語言中的面向對象(1)-類模擬和多態,繼承2005-01-12 10:04出處:作者:liyuming1978>   在面向對象的語言裡面,出現了類的概念。這是編程思想的一種進化。所謂類:是對特定數據的特定操作的集合體。所以說類包含了兩個範疇:數據和操作。而C語言中的struct僅僅是數據的集合。
  • C 語言實現面向對象第一步--對象模型
    這被稱為運行時綁定,在一些動態語言中,鴨子類型(duck typing) 常用來實現這種「多態」— 不關心是什麼東西,只要覺得它可以run,就給他寫個叫 run的函數即可。當然 OOP 也並非萬能藥。= 封裝、繼承、多態。本文僅僅是想討論下在 C 中如何實現封裝、繼承、多態。封裝可以藉助 struct,將數據和方法都放到一個結構體內,使用者可以無需關注具體的實現。
  • C+相比其他語言到底難在哪裡?
    看過程式語言排行榜的都知道,c/c++自02年以來,不管時代如何發展,其排名一直在前五以內,足見其在程式語言界的地位。編程界流行這麼一句話:c幾乎什麼都能做,c++幾乎什麼都能做好,足見其功能的強大。
  • c++虛繼承,多繼承
    多重繼承例3: 請評價多重繼承的優點和缺陷。多重繼承在語言上並沒有什麼很嚴重的問題,但是標準本身只對語義做了規定,而對編譯器的細節沒有做規定。所以在使用時(即使是繼承),最好不要對內存布局等有什麼假設。為了避免由此帶來的複雜性,通常推薦使用複合。
  • 大話go 語言:談談 go 語言的類型系統
    引言:最近地鐵上沒事,準備花一點時間研究下 go 語言,分兩節來分享,這節首選『類型系統』因為我覺得類型系統是一門語言之核心。五年前,曾被面試官面試到 C 和 C++的區別。其中有被問到如何用 C 語言實現面向對象編程。
  • C 語言會比 C++ 快?
    和面向過程的 C 語言相比,其繼承者 C++ 不僅可以進行 C 語言的過程化程序設計,還可以進行以繼承和多態為特點的面向對象的程序設計。要論兩者上手的難易度,對此,有網友評價道,學好 C 只要 1 年,而學好 C++ 需要的可能不止 10 年。