欢迎光临散文网 会员登陆 & 注册

C语言宏定义实现极似面向对象语法,含伪gc

2021-05-15 12:59 作者:陈伟国AE  | 我要投稿

本文之前在百度贴吧发布过,基本没人看,在这试试水。

原文链接:

http://tieba.baidu.com/p/7337300220?share=9105&fr=share&see_lz=1&sfc=copy&client_type=2&client_version=11.4.8.0&st=1620397940&unique=D2CF6E36E7E60F7667D849A1F58E0175 


最近在学m3,m4单片机,都是C语言编程,打了两年的《JVAV》一接触C语言编程环境十分不习惯,打算用C封装一个面向对象基本操作。

目前通过宏定义实现了对象的内存分配,this指针的切换,构造函数的执行等基本功能。

每个类(结构体)都可以通过包含一个通用结构体的方式来实现继承。

由于水平比较菜,gc实现了一个最菜的计数法回收内存,内存不够用的时候根据计数来回收不常用的对象以及该对象动态分配的数据。在单片机环境中运行影响不大,单片机任务注重实时性,长时间不用的内存回收兼容该场景。


先看看运行效果和main函数代码:

main函数代码
运行效果


具体实现:

首先是对象内存分配宏定义new()的实现

我定义了两种

一种是带参数的new_args(type,args),相当于带参数的构造函数

一种是不带参数的new(type),相当于无参构造函数

new()宏定义实现

new实现了之后就是类的结构了,一个类(结构体)中除了你自己要用的函数指针和属性之外,必须额外包括:无参构造函数和有参构造函数以及计数器。

而且由于语法限制,类构造函数名字必须按照如下格式:

例如你的类叫TEST,那构造函数就要叫

void Const_TEST_ARGS(TEST obj,void* str,...);

void Const_TEST(TEST obj);

typedef出来的结构体名字也要按照如下格式:

例如你的类叫TEST,typedef结构体就要叫_TEST

下面放一个TEST的模板和一个List的头文件参考参考:

类结构演示
List类结构


最后就是gc了,这块折磨了我亿小会,由于比较菜不知道realloc重新分配会修改之前的地址导致程序一直 -1073740940

先说下基本思路:

1.用一个List存储new出来的对象

2.再用一个List存储这些对象创建的动态内存空间。

3.写一个通用结构体,里面就放一个int型的计数值,通过对象强转从而得到该对象的计数值

4.采用最简单的计数法实现对象回收,长时间不调用的话到点就会执行回收,并free掉对象指针以及该对象分配的动态内存空间。

5.这里长时间不调用会被回收,那么什么是调用呢?

#define list $(LLL)

#define test $(TTT)

TEST TTT=new(TEST);

List LLL=new(List);

只要使用了list.和test.就算是调用,计数器就会清掉,并且把this指针切换成当前对象

或者你可以这样调用$(LLL).和$(TTT)效果是一样的。

上个图:

首先是gc.h

gc头文件
gc头文件

结构很简单,两个List一个用来装new出来的对象,一个用来装这些对象产生的数据

_Data这个结构体中的Belonger用来存储数据的产生者(是哪个对象创建该数据的),Value用来存储数据指针。到时候删除对象的时候,把Belonger和该对象相同的数据也删了。

然后是函数实现:

gc函数实现

gc_Init()函数中初始化了两个List并开启了线程用于执行gc

gc_Run()里面不断判断每个对象的计数值,超过了阈值就清掉该对象和对应的数据

gc_Clear()提供给用户主动调用,直接清掉你不要的对象

gc_Run()里面不断遍历对象List,通过把List中的对象强转成通用结构体Template来获取该对象的计数值,如果超过了阈值,就清掉该对象,然后去遍历数据List,通过Belonger判断该对象是否创建了数据,如果有,就把其创建的数据也清掉。

逻辑比较简单,可以正常实现基本功能,基本满足单片机任务中的对象回收需求。

最后放个List类里面有关gc的代码

扩容时:

List扩容处理,其中pool就是gc的数据池子

缩容时:

List缩容处理,其中pool就是gc的数据池子

删除数据:

List删除操作,其中pool就是gc的数据池子

当然这代码写出来了肯定是有bug的,水平菜情有可原

宏定义BUG,及其影响手感,目前想不到解决方案

#define list $(LLL)

#define test $(TTT)

TEST TTT=new(TEST);

List LLL=new(List);

使用TTT.a=100;或者TTT.b=10;时会报错,因为我宏定义采用的是表达式复合语句,终究还是表达式,表达式众所周知是不可以被赋值的。

如果使用另一种宏定义方法:

#define V(n) this=n;\

 n->counter=0;\

 (*n)

照样会有bug,bug还比刚才的严重更多,使用这款宏定义后,赋值可以了,但是使用带返回值的函数就会出错,要改也可以,但是代价很大,必须把所有的函数都加上返回值。

代码我直接发出来,带师门帮忙看看

[有效] https://pan.baidu.com/s/1HlZiw0c4vdIunx6GXUPT8g 

提取码

ALYA


如果这里浏览量高的话,下篇我会写一个如何在clion上使用stm32cubemx和stm32标准库,主要是标准库,网上教程比较少,同样的,我会把工程模板发出来。

clion上stm32f4标准库开发
clion开发有多香一目了然

可以看到成功build出了hex和bin文件

C语言宏定义实现极似面向对象语法,含伪gc的评论 (共 条)

分享到微博请遵守国家法律