|||
如何用C语言实现面向对象思想的开发?
现状
目前各行各业中的软件工程师规模庞大,种类繁多,但是有一小众化的软件工程师往往受到忽视,那就是嵌入式软件工程师,对于小众化的嵌入式软件工程师其实门槛还是很高的,要有一定的硬件基础和硬件调试能力,特别是当项目产品不依赖与操作系统平台而进行裸奔的,对嵌入式软件工程师要求更高;
除了上面提到的门槛因素外,大多数嵌入式软件工程师用传统的C语言直面复杂的电路板,当代码量小于1W,软件功能复杂度较小的情况下,传统的C语言思维还能够指导单个软件工程师按部就班的写驱动->调驱动->写应用->调试主体功能;但是代码量突破1W功能较复杂的情况下,这就需要多人协调完成代码编写与功能调试,软件需求->软件架构->软件层次划分->软件模块划分->代码实现->模块级调试->功能模块调试->主功能调试;考虑到实现过程的环节,这就需要在代码实现时要进行必要的抽象与封装,对于传统的C语言是不支持面向对象的,这就让用C语言实现面向对象的封装与抽象的需求应运而生;
实现方法
对于传统的C编程,单个功能模块的实现需要多个C文件的支撑,功能模块间的数据传递是通过全局变量和接口函数的调用实现的;按照C++的类定义,可以用C中的struct 实现部分类的功能;
比如如何用C实现一个CAN驱动的类开发;
A、 用struct 定义属性和方法
typedef struct CanbDriver
{
//属性
struct CanData m_strECanRxData;
struct
CanData m_strECanTxData;
struct CanConfigData m_strECanbConfigData;//配置数据
/方法
Uint32 (*Initialize)(void);
Uint32 (*SendData)(struct CanbDriver
*pThis,Uint16 * pTxAddr,Uint16 uiFrameTypeId);
Uint16 * (*ReceiveData)(struct CanbDriver
*pThis,Uint16 uiFrameTypeId);
}CCanDriver;
B、 在CanDriver.c中实例化各个接口函数
即定义函数
//初始化
Uint32 CAND_Initialize(struct CanbDriver *pThis)
{
Uint32
ulErroCode = 0;
………
//实现
return ulErroCode;
}
//发送
TUint32 CAND_SendData(struct CanbDriver *pThis, ,Uint16 *
pTxAddr,Uint16 uiFrameTypeId)
{
Uint32
ulErroCode = 0;
………
//实现
return ulErroCode;
}
//接收
Uint16 * CAND_ReceiveData)(struct CanbDriver *pThis,Uint16
uiFrameTypeId)
{
Uint16 * pAddr = NULL;
………
//实现
return pAddr;
}
C、 如何将.C文件中实例化的函数对接“类”中的方法呢?
这就要求我们的“构造函数”construct中对各个函数进行注册,也就是创造对象;
//构造函数
void
CCanaDriver_Cnst(CCanaDriver* pObj)
{
pObj-> Initialize = CAND_Initialize;
pObj-> SendData = CAND_SendData;
pObj->ReceiveData = CAND_SendData;
}
D、 当然为了更接近C++的风格我们需要宏定义
#define ConstructObject(c,o) c##_Cnst(o)
当然我们得定义一个实体CCanDriver CanDriverObj;
这样我们就能推导出:
调用ConstructObject(CCanDriver,CanDriverObj)等价于CCanaDriver_Cnst(&CanDriverObj)
E、 对于封装好的接口函数,我们如何访问呢,为此我们还定义了这个函数
CCanDriver *GetInstance(CCanDriver)
{
return
&CanDriverObj;
}
F、 至此我们完成了整个CAN驱动的类封装,比如我现在想初始化一下这个CAN驱动则进行如下操作:
//获取入口
CCanDriver *Obj = GetInstance(CCanDriver);
//访问接口函数
Obj-> Initialize(Obj);
至此我们完成了一个驱动的类封装;
优点?
有人会问,我用之前的方法也能够实现,为啥还要烧脑实现这个?当然仁者见仁智者见智,以我个人的经历而言,这种实现代码的方式能够使代码更加清晰,接口访问清晰明了,杜绝了全局变量的使用,各个模块间通过接口访问;