文章目录

  • 概述
  • 框架介绍
  • 基类
    • c++基类
    • c 实现基类
  • 派生类
    • c++派生类
    • C实现派生类
  • 基于模型的多重继承
    • C++的多重继承
    • c的多重继承
  • 对象创建和使用
    • C++ 应用
    • C应用

概述

  有一种说法是c语言是一门面向过程的语言,其实这种说法是比较浅显的,面向对象是一种编程设计思想,并不是各个编程语言的属性差异。在语法上,C语言支持的oop(面向对象)机制比较薄弱,但完全可以使用c语言写出面向对象的程序,只不过很多细节没有语法支持,需要编程人自己去实现。实际上编程实现机制的方式也并不只有提高工作量和门槛的弊端,它可以是更加灵活的。

  这里使用的实例是一个c版本的面向对象框架,是根据RT-Thread 的rt-robot软件包(c语言)简化,旨在总结c语言的oop。

  RT-Thread的软件包地址:https://github.com/sogwms/rt-robot

  此用例(简化版)地址:https://github.com/AntigravityC/rt-robot-to-cpp
的c-version分支

  我改写的C++版本的 rt-robot 框架程序地址:上述地址的main分支

  我们可以从这两者的对比中了解c语言如何实现面向对象编程

框架介绍

  首先从应用的角度介绍一下这个RT-Thread的rt-robot框架,这是我的一个朋友向rt-thread贡献的开源项目,可以使用此框架对智能车、无人机等机器人模型进行对象创建及拼接版的简单编程。例如:一台具有闭环控制功能的智能车的驱动部分由几个轮子组成、轮子由电机和编码器组成、电机提供输出、编码器提供反馈,再加上指令和pid等控制器算法就可以形成一个简单却经典的闭环控制系统。

  在用c实现这样一个程序时,我们当然可以书写几个电机旋转、编码器读取、控制算法计算等各个模块功能的函数,将信息在它们之间传递,从而实现对应的功能。但是使用面向对象的方式去构建它可以更加具有更好的模块化、低耦合高内聚、高复用性、易维护性和更加灵活。试想我们的小车可能在某一天需要加装一个电机、或者需要换掉所有的编码器,在面向过程的程序中可能牵一发而动全身,而在将这些传感器和执行器作为对象进行构建的程序中做更改将是一件方便并且优雅的事情。

  以电机为例:继承是面向对象的要素,用c++的话我们实现基类 motor,实现派生类:各种类型的电机(双 PWM、单 PWM 的直流电机),如双pwm直流电机dual_motor来继承motor,并添加具体直流电机旋转的功能的方法。编码器也是如此:如使用AB 相编码器来继承编码器基类。

  这样使用此派生类创建对象的时候就创造了一个具有功能的小车轮子驱动。最后根据小车上器件的数量进行对象的创建和车子模型的组合。

  下文将从程序是实现的角度进行对比分析

  注:只展示部分核心程序,如需完整工程请跟进前文提示到github下载

基类

基类是已存在的用来派生新类的类为父类,使用基类我们可以实现通用、复用性接口。

c++基类

//.hpp
class motor
{public:  motor();  ~motor();virtual bool set_speed(int thousands) = 0;virtual bool motor_run(int thousands) = 0;
};
//.cpp
motor::motor()
{}motor::~motor()
{}

这里set_speed和motor_run是纯虚函数,因为这个motor类是抽象基类,创建它是为了提供一个通用性的接口

c 实现基类

  因为C语言没有继承机制,所以实现基类接口需要如下准备工作

//.h
typedef struct motor
{bool(*set_speed)(void *mot, int thousands);bool (*destroy)(void *mot);
}motor_t;motor_t  *motor_create(uint8_t size);
bool motor_destroy(motor_t *mot);
bool motor_run(motor_t *mot, int thousands);

1.空间创建不能像c++直接使用new,而是使用内存申请并层层调用

motor_t *motor_create(uint8_t size)
{motor_t*new_motor = (motor_t*)malloc(size);if (new_motor == NULL){printf("Falied to allocate memory for new motor\n");return NULL;}return new_motor;
}bool motor_destroy(motor_t *mot)
{assert(mot != NULL);  printf("free motor");free(mot);return true;
}

2.通过motor_t *mot这种形参来将创建的对象指针传递进来作为动作主体(主语),并且动作操作(谓语,这里是set_speed)也需要在后续用指针指定方式来继承,以此完成通用接口的实现。

bool motor_run(motor_t *mot, int thousands)
{assert(mot != NULL);mot->set_speed(mot, thousands);return true;
}  

  

派生类

  派生类是利用继承机制,从已有的类中派生的类。

c++派生类

  直流电机dual_pwm_motor继承基类 motor,并将纯虚函数进行实现,相对抽象基类、这里派生类要具有具体的对象的功能操作函数:set_speed里的pwm_set,可理解为硬件层面的使电机旋转的功能操作函数。

//.hpp
class dual_pwm_motor : public motor
{public:dual_pwm_motor();~dual_pwm_motor();char *dev;bool set_speed(int thousands);bool motor_run(int thousands);
};//.cpp
bool dual_pwm_motor::set_speed(int thousands)
{if (thousands == 0){pwm_set(thousands); //do something to make motor run}printf("dual_pwm_motor_set_speed %d\n",thousands);return true;
}bool dual_pwm_motor::motor_run(int thousands)
{set_speed(thousands);return true;
}

C实现派生类

//.h
typedef struct dual_pwm_motor
{struct motor mot;char *dev;
}dual_pwm_motor_t;dual_pwm_motor_t *dual_pwm_motor_create(char *pwm);

  如前文所说,c实现的派生类的空间创建需要层层调用、而通用接口里的操作方法应该用指针指向进行继承,即new_motor->mot.set_speed = dual_pwm_motor_set_speed;

//.cstatic bool dual_pwm_motor_set_speed(void *mot, int thousands)
{dual_pwm_motor_t* mot_sub = (dual_pwm_motor_t*)mot;if (thousands == 0){//pwm_set(thousands);//do something to make motor run}printf("dual_pwm_motor_set_speed %d\n",thousands);return true;
}dual_pwm_motor_t *dual_pwm_motor_create(char *dev)
{dual_pwm_motor_t *new_motor = (dual_pwm_motor_t*)motor_create(sizeof(struct dual_pwm_motor));if (new_motor == NULL){return NULL;}new_motor->dev = dev;new_motor->mot.set_speed = dual_pwm_motor_set_speed;return new_motor;
}

基于模型的多重继承

  前文介绍一个轮子由编码器和电机组成,根据我们前文的具体到型号的电机和编码器等类可以实现派生类wheels,是一个组合可以表现这个闭环系统的类,我们可以使用它进行最终的对象创建和组合。

传入目标速度,读取编码器值得到现在的速度,根据两者差值进行控制计算,最后输出,这就是这个最终类的表现。

C++的多重继承

//.hpp
class wheel : public dual_pwm_motor,public ab_phase_encoder
{public:wheel();~wheel();int target;int measure;int output;bool set_target(int speed); void controller_update(int target,int measure,int *output);void update();};//.cpp
bool wheel::set_target(int speed)
{target = speed;return true;
}
#define KP 1
void wheel::controller_update(int target,int measure,int *output) //这里实现了一个简单的比例P控制
{// do somethig to controlint err = target - measure;*output = KP * err;
}void wheel::update(void)
{measure = read();controller_update(target, measure,&output);motor_run(output);
}

c的多重继承

//.h
typedef struct wheel
{motor_t *w_motor;encoder_t   *w_encoder;int target;int measure;int output;
}wheel_t;wheel_t *wheel_create(motor_t *w_motor, encoder_t *w_encoder);
bool wheel_destroy(wheel_t *whl);
void wheel_update(wheel_t *whl);
bool wheel_set_speed(wheel_t *whl, int speed);//.c
wheel_t *wheel_create(motor_t *w_motor, encoder_t *w_encoder)
{// 1. Malloc memory for wheelwheel_t *new_wheel = (wheel_t*)malloc(sizeof(struct wheel));if (new_wheel == NULL){printf("Falied to allocate memory for new wheel");return NULL;}// 2. Initialize wheelnew_wheel->w_motor = w_motor;new_wheel->w_encoder = w_encoder;return new_wheel;
}bool wheel_destroy(wheel_t *whl)
{assert(whl != NULL);printf("Free wheel");motor_destroy(whl->w_motor);encoder_destroy(whl->w_encoder);free(whl);return true;
}bool wheel_set_speed(wheel_t *whl, int speed)
{assert(whl != NULL);whl->target = speed;return true;
}#define KP 1
void wheel_controller_update(int target,int measure,int *output)
{// do somethig to controlint err = target - measure;*output = KP * err;
}void wheel_update(wheel_t *whl)
{assert(whl != NULL);whl->measure = encoder_read(whl->w_encoder);wheel_controller_update(whl->target, whl->measure,&whl->output);motor_run(whl->w_motor, whl->output);
}

对象创建和使用

C++ 应用

int main(int argc,char *argv[])
{wheel  *left_wheel = new wheel();wheel  *right_wheel = new wheel();
//  cout << "main begin\n" << endl;printf("main begin\n");while(1){left_wheel->set_target(ENCODE_TARGET);right_wheel->set_target(ENCODE_TARGET*2);left_wheel->update();      right_wheel->update();printf("left %d   %d   %d\n",left_wheel->target,left_wheel->measure,left_wheel->output);//printf("right %d   %d   %d\n",right_wheel->target,right_wheel->measure,right_wheel->output);usleep(100000);}while(1);
}

C应用

  最后是c语言实现创建对象并使用,方式是创建具体型号的对象,如电机和编码器,将其“组装”到轮子上

int main(int argc,char *argv[])
{wheel_t** c_wheels = (wheel_t**)malloc(sizeof(wheel_t*) * 2);if (c_wheels == NULL){printf("Failed to malloc memory for wheels");return -1;}dual_pwm_motor_t *left_motor   = dual_pwm_motor_create(NULL);ab_phase_encoder_t *left_encoder  = ab_phase_encoder_create(NULL,0);c_wheels[0] = wheel_create((motor_t*)left_motor,  (encoder_t*)left_encoder);// ...while(1){wheel_set_speed(c_wheels[0] ,100);wheel_update(c_wheels[0] );printf("%d   %d   %d\n",c_wheels[0]->target,c_wheels[0]->measure,c_wheels[0]->output);sleep(1);}return 0;
}

  总结:C实现的面向对象框架,有两点特性需要注意,实现效果是创建时使用具体的模拟类的结构体如dual_motor,而使用时则是不分型号的wheel_update,也就是通用接口。

  注:这个例子是智能车框架,这里最上层一步却只到轮子,看起来有些奇怪,实际上RT-Thread的完整的c语言的机器人框架的最上层是到car的,还有几种不同的型号器件、算法选择等功能、这里是以最简形式举例的,所以只有简化版的驱动部分。

最后感谢开源,让我们共同学习,共同进步。

c语言实现面向对象编程相关推荐

  1. C++学习笔记:(七)C语言实现面向对象编程

    面试官:C和C++有什么不同? 应聘者:一个是面向过程,一个是面向对象. 这样的答案在我看是不完全正确,因为面向过程编程和面向对象编程是编程思想,C++可以用面向过程的思想编程,同样,C语言也可以用面 ...

  2. C 语言实现面向对象编程

    C 语言实现面向对象编程 1.引言 面向对象编程(OOP)并不是一种特定的语言或者工具,它只是一种设计方法.设计思想.它表现出来的三个最基本的特性就是封装.继承与多态.很多面向对象的编程语言已经包含这 ...

  3. 一步步分析-C语言如何面向对象编程

    这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...

  4. ctor c语言,一步步分析-C语言如何面向对象编程

    这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...

  5. 老汤回味——C语言与面向对象编程

    今天的文章我们来看看如何结合面向对象的思想使用C语言写出结构良好的代码.直接看代码,然后我们来分析一下代码中的含义.首先是头文件user.h: #ifndef USER_H #define USER_ ...

  6. Java语言基础-面向对象编程三步走之打开冰箱门

    开头: 何谓"面向对象" 面向对象是一种编程思想. 思想是一个很虚无缥缈的东西,但是它可以从一个人的具体行动中体现出来,如果说你坚持每天跑步,每天读书,做一些有益于身心健康的事情, ...

  7. PHP语言之面向对象编程

    第3关:面向对象编程与数据库综合运用 <?php class model {protected $db; //数据库操作类实例protected $tableName; //待操作的表名prot ...

  8. java语言基础-面向对象编程-方法(廖雪峰老师官网学习记录)

    Java是一种面向对象的编程语言. 面向对象编程,英文是Object-Oriented Programming,简称OOP. 面向对象编程,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方 ...

  9. C语言中面向对象编程

    C语言中面相对象的编程 面向对象的重要思想就是数据隐藏,在面向对象语言中,对象可以包含私有变量.这样我们可以说他们具有内部状态,这些内部状态对其他对象是透明的.全局变量可以通过设置变量作用域来模拟私有 ...

最新文章

  1. java 连接 pgsql
  2. 感觉皮层实质性参与工作记忆的信息保存
  3. Sitecore7.5 安装指南 -- 从.EXE文件安装Sitecore CMS
  4. PHP 入门 - 3.字符串
  5. 具有Infinispan的聚集幂等消费者模式
  6. 电脑服务器系统被管理员停用,Win7系统任务管理器已被系统管理员停用怎么办?...
  7. 初学Java,第一段代码
  8. 20190823 尚硅谷MySQL核心技术
  9. 【重点】Batch Normalization的诅咒
  10. cpu_time()函数
  11. python函数参数用法_Python:函数参数类型及其用法
  12. 2019小程序没必要做了_2019年,小程序还要不要做
  13. ubuntu16.04.4环境下mingw32交叉编译环境搭建
  14. 为软件简单加密的小程序,附源码
  15. 加性噪声(目前不理解)
  16. 【OMNeT++】ALOHA协议仿真中的channelUtilization
  17. 20190328-几种数据清洗的方法
  18. UE4地形使用卫星贴图
  19. [艾兰岛]制作传送门之搭建系列——kura酱长期更新
  20. 51入门_1602液晶

热门文章

  1. 全国计算机等级考试python题库
  2. 从中专生到哈工大教授,他是一个自学成才的励志楷模!
  3. 贝壳找房面试之数据库
  4. 通信原理之模拟幅度调制(线性调制)详解
  5. typing‘ has no attribute ‘_SpecialForm‘
  6. webkt内核和gecko内核比较
  7. 深度学习中epoch,batch的概念--笔记
  8. 东呈国际集团决定对湖北省加盟酒店进行重大费用减免
  9. uniapp ios内购支付问题(返回订单信息失败)
  10. 企业撩文化下的品牌推广策略