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 语言实现的容器,我们可以管理内核对象,容器中包含的内核对象有对象本身的链表,拿线程打比方,我们新创建的线程也就可以通过链表的形式挂接到容器中对应的线程控制块中,实现的效果如下:
最后,如果您觉的我的文章对您有所帮助,可以关注我的个人公众号,期待与您一同前行~
C语言如何实现继承及容器的更多相关文章
- 模块的封装之C语言类的继承和派生
[交流][微知识]模块的封装(二):C语言的继承和派生 在模块的封装(一):C语言的封装中,我们介绍了如何使用C语言的结构体来实现一个类的封装,并通过掩码结构体的方式实 现了类成员的保护.这一部分,我 ...
- C语言设计模式-封装-继承-多态
快过年了,手头的工作慢慢也就少了,所以,研究技术的时间就多了很多时间,前些天在CSDN一博客看到有大牛在讨论C的设计模式,正好看到了,我也有兴趣转发,修改,研究一下. 记得读大学的时候,老师就告诉我们 ...
- django基础 -- 4. 模板语言 过滤器 模板继承 FBV 和CBV 装饰器 组件
一.语法 两种特殊符号(语法): {{ }}和 {% %} 变量相关的用{{}},逻辑相关的用{%%}. 二.变量 1. 可直接用 {{ 变量名 }} (可调用字符串, 数字 ,列表,字典,对象等) ...
- [笔记]使用Go语言Redigo包在Docker容器内连接Redis容器的方法
Docker容器之间的连接可以带来不少方便,下面记录下如何在自己容器内通过环境变量连接与之连接的Redis容器的方法. 先起一个Redis的Docker容器,命名为 redis,再起一个自己的Dock ...
- OOP3(继承中的类作用域/构造函数与拷贝控制/继承与容器)
当存在继承关系时,派生类的作用域嵌套在其基类的作用域之内.如果一个名字在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义 在编译时进行名字查找: 一个对象.引用或指针的 ...
- Django框架(十一):模板介绍、模板语言、模板继承、HTML转义
1. 模板介绍 1.1 模板的功能 产生html,控制页面上展示的内容.模板文件不仅仅是一个html文件. 模板文件包含两部分内容: 静态内容:css.js.html. 动态内容:用于动态去产生一些页 ...
- C语言实现类似C++的容器vector
C语言也能面向对象?不是C++是面向对象的么?其实C语言也能抽象成简单的面向对象方法,在Linux内核源码当中,底层的驱动代码.文件系统等皆采用了面向对象的封装技术,这样的好处是将客观的东西抽象出来, ...
- C++语言基础(9)-继承
一.继承的基本语法 #include<iostream> using namespace std; //基类 Pelple class People{ public: void setna ...
- 《Java语言程序设计》继承与多态
一.动手实验:继承条件下的构造方法 调用运行 TestInherits.java 示例,观察输出,注意总结父类与子类之间构造方法的调用关系修改Parent构造方法的代码,显式调用GrandParent ...
随机推荐
- SpringBoot 集成Web
1,静态资源访问: 在我们开发Web应用的时候,需要引用大量的js.css.图片等静态资源. 默认配置 Spring Boot默认提供静态资源目录位置需置于classpath下,目录名需符合如下规则: ...
- vue-父组件传递参数到子组件
案例: 父组件 <template> <div id="app"> <h1>vuex</h1> <h3>count:{{ ...
- pthread_rwlock_t
一.读写锁 读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作. 读操作可以共享,写操作是排他的,可以有多个在读(与 CP ...
- 模块 shutil_zipfile_tarfile压缩解压
shutil_zipfile_tarfile压缩解压 shutil 模块 高级的 文件.文件夹.压缩包 处理模块 shutil.copyfileobj(fsrc, fdst[, length]) #将 ...
- Codeforces 杂题集 2.0
记录一些没有写在其他随笔中的 Codeforces 杂题, 以 Problemset 题号排序 1326D2 - Prefix-Suffix Palindrome (Hard version) ...
- 手工注入——access手工注入实战和分析
今天进行了access手工注入,下面是我的实战过程和总结. 实战环境使用的是墨者学院的在线靶场.下面咱们直接进入主题. 第一步,判断注入点 通过‘ 或者 and 1=1 和 and 1=2 是否报错, ...
- C 电压
时间限制 : 10000 MS 空间限制 : - KB 评测说明 : 1s,256m 问题描述 JOI社的某个实验室中有着复杂的电路.电路由n个节点和m根细长的电阻组成.节点被标号为1~N ...
- 华为五年自动化测试工程详细解说:unittest单元测试框架
一.单元测试框架说明 单元测试是指在编程中,针对程序模块的最小单元(类中的方法)进行正确性检验的测试工作.python+selenium自动化测试中通常使用unittest或者pytest作为单元 ...
- HTML5+CSS+JQuery 实现简单的进度条功能
样式: <style type="text/css"> .processcontainer2{ width:450px; border:1px solid #6C9C2 ...
- easy-mock 本地部署(挤需体验三番钟,里造会干我一样,爱象节款mock)
前言 很多小伙伴问我怎么在自己公司的项目里面添加配置mock,在vue项目里面都知道怎么配置mock,在大型前端项目里面就一脸疑惑了. 我就回答他,你今天会在vue项目里面用,那天换公司是用angul ...