搜索
bottom↓
回复: 89

C 语言面向对象开发,用的人多吗?

  [复制链接]

出0入0汤圆

发表于 2015-3-14 19:50:20 | 显示全部楼层 |阅读模式
本帖最后由 zhuser 于 2015-3-14 19:56 编辑

在新的项目里用了面向对象的思路来做封装.

感觉系统的脉络更容易理清了.代码更易复用.好处多多.

大家用得多不多?交流一下.

阿莫论坛20周年了!感谢大家的支持与爱护!!

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。

出0入0汤圆

发表于 2015-3-14 20:05:05 | 显示全部楼层
直接用C++不就可以了吗?

出0入0汤圆

 楼主| 发表于 2015-3-14 20:51:42 | 显示全部楼层
wangyu_2011 发表于 2015-3-14 20:05
直接用C++不就可以了吗?

有道理.

目前,C++还玩不转.

出0入0汤圆

发表于 2015-3-14 20:58:43 | 显示全部楼层
具体,展开说说

出0入0汤圆

发表于 2015-3-14 21:02:51 | 显示全部楼层
C里边将变量,函数统一放到结构体里,对应于C++的class,没多大区别。

出0入0汤圆

发表于 2015-3-14 21:17:16 | 显示全部楼层
wangyu_2011 发表于 2015-3-14 20:05
直接用C++不就可以了吗?

正解。
用C++就可以了,面向对象C++还要便利得多。

出0入54汤圆

发表于 2015-3-14 21:34:29 | 显示全部楼层
azeng 发表于 2015-3-14 21:02
C里边将变量,函数统一放到结构体里,对应于C++的class,没多大区别。

那函数能放到结构里?

出0入0汤圆

发表于 2015-3-14 21:38:26 | 显示全部楼层
unifax001 发表于 2015-3-14 21:34
那函数能放到结构里?

应该是函数的入口地址   什么时候放过一段的内存

出0入0汤圆

发表于 2015-3-14 21:46:10 来自手机 | 显示全部楼层
楼主可以看看OOPC和傻孩子的贴子

出0入4汤圆

发表于 2015-3-14 21:47:51 | 显示全部楼层
azeng 发表于 2015-3-14 21:02
C里边将变量,函数统一放到结构体里,对应于C++的class,没多大区别。

“没多大区别?”你深入学习之后,再比较一下。

出0入0汤圆

发表于 2015-3-14 22:14:15 | 显示全部楼层
我用了。既然是交流,楼主就讲讲你是怎么做的?

出0入0汤圆

发表于 2015-3-14 22:20:38 | 显示全部楼层
#ifndef __X_OOPC_H__
#define __X_OOPC_H__


//用于定义Class的宏.比如定义Class名为A,则实际上定义了一个结构体__A和一个联合体A
#define DEF_CLASS(__NAME, ...)                                                  \
    typedef union __NAME __NAME;                                                \
    __VA_ARGS__                                                                 \
    typedef struct __##__NAME __##__NAME;                                       \
    struct __##__NAME {

#define END_DEF_CLASS(__NAME)                                                   \
    };                                                                          \
    union __NAME {                                                              \
        __##__NAME;                                                             \
        uint_fast8_t chMask[(sizeof(__##__NAME) + sizeof(uint_fast8_t) - 1) / sizeof(uint_fast8_t)];\
    };

//用于声明公共Class的宏.这个Class可以被继承
#define EXTERN_PUBLIC_CLASS(__NAME, ...)                                        \
    typedef union __NAME __NAME;                                                \
    __VA_ARGS__                                                                 \
    typedef struct __##__NAME __##__NAME;                                       \
    struct __##__NAME {

#define END_EXTERN_PUBLIC_CLASS(__NAME)                                         \
    };                                                                          \
    union __NAME {                                                              \
        __##__NAME;                                                             \
        uint_fast8_t chMask[(sizeof(__##__NAME) + sizeof(uint_fast8_t) - 1) / sizeof(uint_fast8_t)];\
    };

//用于声明私有Class的宏.这个Class不可被继承.比如声明一个Class名为A,实际上定义了一个联合体A
#define EXTERN_CLASS(__NAME,...)                                                \
    typedef union __NAME __NAME;                                                \
    __VA_ARGS__                                                                 \
    union __NAME {                                                              \
        uint_fast8_t chMask[(sizeof(struct {

#define END_EXTERN_CLASS(__NAME)                                                \
        }) + sizeof(uint_fast8_t) - 1) / sizeof(uint_fast8_t)];                 \
    };

//用于定义使用Class的宏.比如Class名为A,真正的Class是名为__A的结构体
#define CLASS(__NAME)               __##__NAME




//用于继承Class的宏
#define INHERIT(__TYPE)             INHERIT_EX(__TYPE, base);

#define INHERIT_EX(__TYPE, __NAME)                                              \
    union {                                                                     \
        __TYPE;                                                                 \
        __TYPE  __NAME;                                                         \
    };




//用于定义接口的宏
#define DEF_INTERFACE(__NAME, ...)                                          \
    typedef struct __NAME __NAME;                                           \
    __VA_ARGS__                                                             \
    struct __NAME {

#define END_DEF_INTERFACE(__NAME)                                           \
    };


#endif // X_OOPC

这个只是外表,可以不要,关键是面向对象思想。C++的高级特性对嵌入式来说效率低,不划算。

出0入0汤圆

发表于 2015-3-14 22:21:21 | 显示全部楼层
zhuser 发表于 2015-3-14 20:51
有道理.

目前,C++还玩不转.

只用C++面向对象那部分

出0入0汤圆

发表于 2015-3-14 22:36:29 | 显示全部楼层
ijlc1314 发表于 2015-3-14 21:46
楼主可以看看OOPC和傻孩子的贴子

有OOPC的帖子链接不

出0入0汤圆

发表于 2015-3-15 08:53:30 来自手机 | 显示全部楼层
本帖最后由 sbk100 于 2015-3-15 08:55 编辑

不要用c++! 不好学!纯c开发图形界面可以去学gtk

出0入0汤圆

发表于 2015-3-15 09:15:11 | 显示全部楼层
面向对象是一种思想, 另外可以看一些设计模式,如MVC等, 看 敏捷软件开发 提到的准则如针对接口编程等, 对设计都是很有帮助的

出0入0汤圆

发表于 2015-3-15 09:23:47 来自手机 | 显示全部楼层
嵌入式用c++难度还是很低的,效率没有想象中的低,自我感觉和c差不多。话又说回来现在stm32多nb,早些年iar就可以在51上编写c++了。stm32这类高级单片机肯定是没有问题的

出0入0汤圆

发表于 2015-3-15 09:51:11 | 显示全部楼层
别人已经踩好的路了.没必要在在一个方向上从新踩一次.而且两个嵌入式里,大部分的开发工具都是直接支持的C++的.
Audrino的库函数也是基于C++封装的.里面有一些东西做的还是真的不错的.
C++只是基本库会占一些ROM空间,大约20K~40K,这对于原来的51来说是多了一点,但对于STM32就完全可以接受了.

出0入0汤圆

发表于 2015-3-15 09:54:16 来自手机 | 显示全部楼层
rt-thread的内核代码就是用c的面向对象做的,你看看作者的rtt文档就知道了,作者有介绍,也可以看看代码的

出110入93汤圆

发表于 2015-3-15 11:34:23 | 显示全部楼层
myxiaonia 发表于 2015-3-15 09:54
rt-thread的内核代码就是用c的面向对象做的,你看看作者的rtt文档就知道了,作者有介绍,也可以看看代码的 ...

是啊,我第一次看到C 的OOP,也是在 RTT。
不过,思路还没理清,只是简单的用结构体。

出0入0汤圆

发表于 2015-3-15 12:53:41 | 显示全部楼层
只用C++的一部分即可。

出0入0汤圆

发表于 2015-3-15 12:57:29 | 显示全部楼层
C 语言用来做面向对象开发,只会得其形不得其神,还是直接C++好。。。。

出0入0汤圆

发表于 2015-3-15 13:55:54 | 显示全部楼层
C做面向对象,还不如改用C++好了

采用面向对象的思想这无问题,但C本身不是为面向对象做准备的

出0入0汤圆

发表于 2015-3-15 15:06:10 来自手机 | 显示全部楼层
可以借鉴思想,但有锤子就不要用螺丝刀去敲钉子

出0入0汤圆

发表于 2015-3-15 16:51:00 | 显示全部楼层
还是直接用C++好 C编多了 自然就会有种封装的冲动 学C++手到擒来的感觉

出100入85汤圆

发表于 2015-3-15 21:09:17 | 显示全部楼层
能不能给个简单的例子说明一下啊

能展示你的想法就行

出0入8汤圆

发表于 2015-3-15 22:26:55 来自手机 | 显示全部楼层
貌似面向对象和用啥语言无关

出0入0汤圆

发表于 2015-3-15 22:30:03 | 显示全部楼层
unifax001 发表于 2015-3-14 21:34
那函数能放到结构里?

猜猜这是谁家的代码:

  1. typedef struct _DEVICE_PROP
  2. {
  3.   void (*Init)(void);
  4.   void (*Reset)(void);
  5.   void (*Process_Status_IN)(void);
  6.   void (*Process_Status_OUT)(void);
  7.   RESULT (*Class_Data_Setup)(uint8_t RequestNo);
  8.   RESULT (*Class_NoData_Setup)(uint8_t RequestNo);
  9.   RESULT  (*Class_Get_Interface_Setting)(uint8_t Interface, uint8_t AlternateSetting);
  10.   uint8_t* (*GetDeviceDescriptor)(uint16_t Length);
  11.   uint8_t* (*GetConfigDescriptor)(uint16_t Length);
  12.   uint8_t* (*GetStringDescriptor)(uint16_t Length);
  13.   void* RxEP_buffer;
  14.   uint8_t MaxPacketSize;
  15. }DEVICE_PROP;
复制代码

出0入0汤圆

发表于 2015-3-15 22:35:29 | 显示全部楼层
ibichao 发表于 2015-3-14 21:47
“没多大区别?”你深入学习之后,再比较一下。

这要看用在什么上面了。公有、私有,继承、多态等确实不好实现。只是单片机里用的话,模仿一下class就够用了的感觉...

出0入0汤圆

发表于 2015-3-15 22:48:32 | 显示全部楼层
zhenghe 发表于 2015-3-14 22:20
#ifndef __X_OOPC_H__
#define __X_OOPC_H__

这不是傻孩子代码嘛~ 感觉用起来怎么样啊?

出10入46汤圆

发表于 2015-3-16 09:13:14 | 显示全部楼层
C 的命名规则定义好。
可以极大的提高开发效率

出0入0汤圆

 楼主| 发表于 2015-3-16 09:22:32 | 显示全部楼层
zhenghe 发表于 2015-3-14 22:14
我用了。既然是交流,楼主就讲讲你是怎么做的?

初衷是因为要用一块板子实现两种不同应用的设备。

暂时还只是用结构体来封装。

以温度为例.以前可能是一堆全局变量.现在
           this_device.AD.Temperature.mV        // 电压值
        this_device.AD.Temperature.k        // 斜率
        this_device.AD.Temperature.b        // 截距
       
另外,就是用get,set.限制全局变量的使用。

感觉改代码,比之前容易多了。

较小的工程。用面向过程,方便很多.

考虑到复用,面向对象效率更高。


出0入0汤圆

 楼主| 发表于 2015-3-16 09:30:59 | 显示全部楼层
gonboy 发表于 2015-3-16 09:13
C 的命名规则定义好。
可以极大的提高开发效率

最近在看代码大全。

不可错过的书。

出0入0汤圆

 楼主| 发表于 2015-3-16 09:36:21 | 显示全部楼层
whatcanitbe 发表于 2015-3-15 21:09
能不能给个简单的例子说明一下啊

能展示你的想法就行

接着上面的 this_device.AD.Temperature 来说。

温度有摄氏温度,开氏温度.

在结构体里加入函数指针。用来做单位转换。

这个代码的复用性就很强了。

出0入0汤圆

发表于 2015-3-16 10:03:03 | 显示全部楼层
涨姿势了,c++是很好,不过不好学呀。。。

出0入17汤圆

发表于 2015-3-16 10:52:40 | 显示全部楼层
C++的继承多态才精华

出0入0汤圆

发表于 2015-3-16 10:55:32 | 显示全部楼层
unifax001 发表于 2015-3-14 21:34
那函数能放到结构里?

可以放函数指针吧

出0入0汤圆

发表于 2015-3-16 13:32:28 | 显示全部楼层
azeng 发表于 2015-3-15 22:30
猜猜这是谁家的代码:

STM32的?

出0入0汤圆

发表于 2015-3-16 13:44:19 | 显示全部楼层
现在的STM32的其他编辑器好像已经支持C++ ,比如EMBED 这类

出0入0汤圆

发表于 2015-3-16 14:15:58 | 显示全部楼层
你真正想表达什么? 是模块划分的思路吗? 比如做移动电源,显示,按键,充电,放电,检测AD 各为一个模块,各成一个对象,还是你用了C++?

出0入0汤圆

发表于 2015-3-17 09:46:21 | 显示全部楼层
如果想要C++做嵌入方面的,看ecos就很好。
rtems也有c++api,不过资料什么的少。

出0入0汤圆

发表于 2015-3-17 10:45:32 | 显示全部楼层
用IAR EWARM7.1 编了下STM32的一个AD采集的小工程,是用纯C写的,结果是:
用C编译
5 460 bytes of readonly  code memory
    124 bytes of readonly  data memory
  2 312 bytes of readwrite data memory
用C++编译
  5 088 bytes of readonly  code memory
    124 bytes of readonly  data memory
  2 205 bytes of readwrite data memory

结果有点意外

出0入0汤圆

 楼主| 发表于 2015-3-17 12:54:59 | 显示全部楼层
wind2100 发表于 2015-3-16 14:15
你真正想表达什么? 是模块划分的思路吗? 比如做移动电源,显示,按键,充电,放电,检测AD 各为一个模块 ...

模块只是一方面.

主要是封装带来的复用,阅读,查看的便利.

目前用C.

出0入0汤圆

 楼主| 发表于 2015-3-17 12:55:31 | 显示全部楼层
一夕nandy 发表于 2015-3-17 10:45
用IAR EWARM7.1 编了下STM32的一个AD采集的小工程,是用纯C写的,结果是:
用C编译
5 460 bytes of readonl ...

下次我也试试看.

出0入0汤圆

发表于 2015-3-17 20:43:27 | 显示全部楼层

恭喜你都会抢答啦~   stm32f1 usb stack

出0入0汤圆

发表于 2015-3-17 20:45:01 | 显示全部楼层
目前纯C开发

出0入0汤圆

发表于 2015-3-25 21:39:36 | 显示全部楼层
本帖最后由 独孤帅 于 2015-3-25 21:42 编辑

贴一段代码,在实际项目项目开发中使用的C面向对象
typedef struct Axis{
        StepMotor_AxisNumber                AxisNumber;
        StepMotor_Direction                 DesDirection;
        Uint16                                 DesSpeed;                                //Pulse PerSecond
        Uint32                                DesDisplacement;                        //Pulse
        int32                                        ThenLocation;                        //Pulse Location
        StepMotor_AxisLimitStatus        ThenLimitStatus;
        FlagBit                                ImmediateStopFlag;
        FlagBit                                LimitPolarFlag;                        //LimitPolar
        Uint16*                                InSendFlag_p;
        void                                        (*StopPulse)        (void);        //CallBack
        StepMotor_AxisLimitStatus        (*GetLimit )        (void);
        void                                        (*ChangeSpeed)        (Uint16,FlagBit);
        void                                        (*SendPulse)        (Uint32);
}StepMotor_Axis;

出0入0汤圆

发表于 2015-3-25 22:35:15 来自手机 | 显示全部楼层
程序大了是得用用

出0入0汤圆

发表于 2015-3-25 23:05:18 | 显示全部楼层
本帖最后由 yiyu 于 2015-3-25 23:06 编辑

不用多态的话,还是少用函数指针吧,
链表节点, 虚函数, 多级继承和回调,对RAM来说还是巨大的开销,

出0入0汤圆

发表于 2015-3-25 23:10:22 | 显示全部楼层
yiyu 发表于 2015-3-25 23:05
不用多态的话,还是少用函数指针吧,
链表节点, 虚函数, 多级继承和回调,对RAM来说还是巨大的开销, ...

讲的对,这就是为啥我仍然推荐用纯C,OOPC是不错的选择。

出100入85汤圆

发表于 2015-3-26 09:47:27 | 显示全部楼层
独孤帅 发表于 2015-3-25 21:39
贴一段代码,在实际项目项目开发中使用的C面向对象
typedef struct Axis{
        StepMotor_AxisNumber                AxisNumb ...

我也想这么弄,不过没有成功。
请问你这样弄的话,具体到某个轴的参数和函数是怎么处理的

比如StepMotor1?

还有如果需要插补的时候这种结构又是怎么处理的?

出0入0汤圆

发表于 2015-3-26 09:56:34 | 显示全部楼层
本帖最后由 独孤帅 于 2015-3-26 10:00 编辑
whatcanitbe 发表于 2015-3-26 09:47
我也想这么弄,不过没有成功。
请问你这样弄的话,具体到某个轴的参数和函数是怎么处理的


由Stepmotor.c文件中的其他函数来维护这个结构体
例如:
/* 各轴属性初始化 */
StepMotor_Axis_D1.DesDirection = DIRECTION_POSITIVE;
StepMotor_Axis_D1.DesDisplacement = 0;
StepMotor_Axis_D1.DesSpeed = 0;
StepMotor_Axis_D1.ThenLimitStatus.PositiveLimitStatus = NoLimit;
StepMotor_Axis_D1.ThenLimitStatus.MiddleLimitStatus = NoLimit;
StepMotor_Axis_D1.ThenLimitStatus.NegetiveLimitStatus = NoLimit;
StepMotor_Axis_D1.ImmediateStopFlag = Flag_False;
StepMotor_Axis_D1.ThenLocation = 0;
StepMotor_Axis_D1.AxisNumber = Axis_D1;
StepMotor_Axis_D1.StopPulse = &PWM_Pwm1AStopPulse;
StepMotor_Axis_D1.LimitPolarFlag = ACTIVE_HIGH;
StepMotor_Axis_D1.InSendFlag_p = &Pwm1InSendFlag;
StepMotor_Axis_D1.GetLimit = &StepMotor_AxisD1GetLimit;
StepMotor_Axis_D1.ChangeSpeed = &PWM_Pwm1ChangeSpeed;
StepMotor_Axis_D1.SendPulse = (void(*)(Uint32))&PWM_Pwm1ASendPulse;

StepMotor_Axis_D2.SendPulse = (void(*)(Uint32))&PWM_Pwm2ASendPulse;        // 不同的轴,给予不同的函数指针

void StepMotor_SetParameter(StepMotor_Axis* Axis, StepMotor_Parameter Parameter,
                Uint32 Number) {
        if (Axis == NULL)
                return;
        switch (Parameter) {
        case DesSpeed:
                Axis->DesSpeed = Number;
                break;
        case DesDisplacement:
                Axis->DesDisplacement = Number;
                break;
        case DesDirection:
                Axis->DesDirection = Number;
                break;
        case LimitPolarFlag:
                Axis->LimitPolarFlag = (FlagBit) Number;
                break;
        }
}

void StepMotor_AxisStop(StepMotor_Axis* AxisHandle) {        // 通过轴的指针,可以直接做操作
        AxisHandle->StopPulse();
}

涉及到插补时,直接调用这些接口函数实现

出100入85汤圆

发表于 2015-3-26 13:27:38 | 显示全部楼层
独孤帅 发表于 2015-3-26 09:56
由Stepmotor.c文件中的其他函数来维护这个结构体
例如:
/* 各轴属性初始化 */

非常感谢。是不是可以这么理解?

stepmotor.c  stepmotor.h相当于提供一个步进电机控制的结构体及其函数
typedef struct
{
...
...
}STEPMOTOR;
而实际的轴X,Y,Z,U
以X轴为例

stepmotorx.c stepmotor.h

STEPMOTOR StepMotor_X;

void stepmotorx_init()
{
  在这里实现X轴变量和函数的初始化???
}

//函数实现
void stepmotorx_fun1()
{

}

void stepmotorx_fun2()
{

}


如果要实现插补的话,比如两轴直线,三轴直线怎么组织文件?
分开line2.c line2.h      line3.c line3.h
line2_inp(0, 200, 300)
line2_inp(1, 200, 300)
line3_inp(0, 200, 300, 200)

还是line.c line.h
line_sel(1,1,0, 0) 选择XY两轴
line_speed()
line_acc()
line_dec()
line_inp(200, 300) 实现XY插补
  
line_sel(1,0,1, 0) 选择XZ两轴
line_speed()
line_acc()
line_dec()
line_inp(200, 300) 实现XZ插补

line_sel(1,1,1, 0) 选择XYZ两轴
line_speed()
line_acc()
line_dec()
line_inp(200, 300,200) 实现XYZ插补

出0入0汤圆

发表于 2015-3-26 17:05:04 | 显示全部楼层
zhuser 发表于 2015-3-14 20:51
有道理.

目前,C++还玩不转.

别胡说!C++ 玩不转,还能用 C 来搞面向对象?
就像一个工具你都不会用,甚至都没看过,还提什么造这个工具?

出0入0汤圆

发表于 2015-3-26 18:44:37 | 显示全部楼层
本帖最后由 独孤帅 于 2015-3-26 23:38 编辑
whatcanitbe 发表于 2015-3-26 13:27
非常感谢。是不是可以这么理解?

stepmotor.c  stepmotor.h相当于提供一个步进电机控制的结构体及其函数 ...


为了方便理解,我选择使用简单的拓扑结构:文件分层,每个文件只能访问比它低一级的文件,同层之间不能互相访问,如果需要则提供回调函数接口,由上层调用时传入。
以插补为例:
应用层(板级操作函数,由主函数调用):Board.c Board.h
中间层(提供插补函数):Line.c Line.h
底层(电机控制函数):StepMotor.c StepMotor.h
驱动层(对DSP寄存器操作的封装):PWM.c PWM.h GPIO.C GPIO.h

由于同层之间不能互相调用,就避免了文件组织的冲突,直接由插补函数统一调用

StepMotor.h
typedef enum{
        Axis_1,Axis_2,Axis_3,Axis_4
}StepMotor_AxisNumber

Board.c
void Board_xxx(void)
{
        ..............
        Line_RunAsLine(StepMotor_GetAxisHandle(Axis_1),StepMotor_GetAxisHandle(Axis_4),5000,45);
        ..............
}

Line.c
void Line_RunAsLine(Axis* Axis_1,Axis* Axis_2,Uint32_u Length,float Angle)        // 轴指针由更上层的应用层函数提供
{
        Uint32_u Axis_1_Dis = 0,Axis_2_Dis = 0;
        Uint32_u Axis_1_Speed = 0,Axis_2_Speed = 0;
        while(插补未结束)
        {
                /* 计算当前位移和速度 */
                ........
                //--------------------------------
               
                Axis_1->SetDisplacement(Axis_1_Dis);
                Axis_2->SetDisplacement(Axis_2_Dis);
                Axis_1->SetSpeed(Axis_1_Speed);
                Axis_2->SetSpeed(Axis_2_Speed);
                Axis_1->Run();
                Axis_2->Run();
                WaitFinish();        // 也可以使用在底层中断里加回调函数的方式实现中断插补
        }
       
        return ;
}

出0入0汤圆

发表于 2015-3-26 19:44:51 | 显示全部楼层
多年以前,不经意间,已经这么用。

出0入0汤圆

发表于 2015-3-26 22:48:07 来自手机 | 显示全部楼层
长知识了,主要是结构体的应用

出100入85汤圆

发表于 2015-3-27 13:22:20 | 显示全部楼层
独孤帅 发表于 2015-3-26 18:44
为了方便理解,我选择使用简单的拓扑结构:文件分层,每个文件只能访问比它低一级的文件,同层之间不能互 ...

学习了,不错。
后生可畏啊

出0入0汤圆

发表于 2015-3-27 14:11:58 | 显示全部楼层
独孤帅 发表于 2015-3-26 18:44
为了方便理解,我选择使用简单的拓扑结构:文件分层,每个文件只能访问比它低一级的文件,同层之间不能互 ...

帅!!10字节啊10字节

出0入0汤圆

 楼主| 发表于 2015-3-30 13:24:28 | 显示全部楼层
Cliff 发表于 2015-3-26 17:05
别胡说!C++ 玩不转,还能用 C 来搞面向对象?
就像一个工具你都不会用,甚至都没看过,还提什么造这个工 ...

哈哈.

工具
        语言
                思想

出0入0汤圆

 楼主| 发表于 2015-3-30 13:33:11 | 显示全部楼层
独孤帅 发表于 2015-3-26 09:56
由Stepmotor.c文件中的其他函数来维护这个结构体
例如:
/* 各轴属性初始化 */

点个赞!

可以优化一下:

把 ini_StepMotor 定义为一个常量.按上面的语句初始化后.

StepMotor_Axis_D1 = ini_StepMotor;

出0入0汤圆

 楼主| 发表于 2015-3-30 13:34:40 | 显示全部楼层
独孤帅 发表于 2015-3-26 18:44
为了方便理解,我选择使用简单的拓扑结构:文件分层,每个文件只能访问比它低一级的文件,同层之间不能互 ...

另外,你的系统里是如何处理不同层抛出的错误的?

出0入0汤圆

 楼主| 发表于 2015-3-30 13:35:28 | 显示全部楼层
相由心生 发表于 2015-3-25 23:10
讲的对,这就是为啥我仍然推荐用纯C,OOPC是不错的选择。

下一步看  "UML+OOPC嵌入式C语言开发精讲".

出0入0汤圆

 楼主| 发表于 2015-7-28 09:40:06 | 显示全部楼层
效果不错。

逻辑层与UI层分离。

普遍使用get,set.

不用全局变量。

代码好改多了。。。

出0入0汤圆

发表于 2015-7-30 17:13:13 | 显示全部楼层
面向对象不一定要局限于语法或者是语言。市面上的OOPC的模块(宏,掩膜结构体等),都相对来说只是制造了面向对象的假象。真正面向对象是对对象的抽象化,封装成类。这也是C语言说的模块化编程的一种基本体现。

出0入0汤圆

发表于 2015-7-30 21:45:34 | 显示全部楼层
zhuser 发表于 2015-3-14 20:51
有道理.

目前,C++还玩不转.

单片机 用C++ ?

出0入0汤圆

发表于 2015-7-30 22:22:50 | 显示全部楼层
独孤帅 发表于 2015-3-26 18:44
为了方便理解,我选择使用简单的拓扑结构:文件分层,每个文件只能访问比它低一级的文件,同层之间不能互 ...

应用层(板级操作函数,由主函数调用):Board.c Board.h
中间层(提供插补函数):Line.c Line.h
底层(电机控制函数):StepMotor.c StepMotor.h
驱动层(对DSP寄存器操作的封装):PWM.c PWM.h GPIO.C GPIO.h

不错,这样移植会方便很多

看来我就拿N个LED的操作来练吧

出0入0汤圆

发表于 2015-7-30 22:33:12 | 显示全部楼层
封装数据  + 回调函数 就好了  继承什么根本不用的!又继承 又多态的 纯属乱用!!

出0入0汤圆

 楼主| 发表于 2015-8-4 08:46:55 | 显示全部楼层
zhangcoipklll85 发表于 2015-7-30 17:13
面向对象不一定要局限于语法或者是语言。市面上的OOPC的模块(宏,掩膜结构体等),都相对来说只是制造了面 ...

握爪,OOPC。

出0入0汤圆

 楼主| 发表于 2015-8-4 08:48:19 | 显示全部楼层

IAR有编译器支持。不妨一试。

出0入0汤圆

 楼主| 发表于 2015-8-4 08:51:19 | 显示全部楼层
728196 发表于 2015-7-30 22:33
封装数据  + 回调函数 就好了  继承什么根本不用的!又继承 又多态的 纯属乱用!! ...

尺有所短,寸有所长。

任何一种语言,方法的出现,都是为解决开发过程中的问题而生的。

选择适合自己的语言,方法即可。

不妨讲一下你的心得。

出0入0汤圆

 楼主| 发表于 2015-8-4 09:04:59 | 显示全部楼层
另一个心得,好的代码应该具有自述性。

哪怕只有少数的注释。

看上下文,也知道过程是如何执行的,重点在哪里。

出0入0汤圆

发表于 2015-8-4 13:22:55 | 显示全部楼层
多点用继承等C++特性,会好多多的

出0入0汤圆

发表于 2015-8-4 13:37:37 | 显示全部楼层
本帖最后由 cddx 于 2015-8-4 13:45 编辑

面向对象三大特性;封装、继承、多态。C能完成多少呢?能容易优雅地完成吗?如果只能用C笨拙完成部分工作,你也硬要说成是面向对象的话,我只能无语了。

出0入0汤圆

 楼主| 发表于 2015-8-4 17:01:23 | 显示全部楼层
本帖最后由 zhuser 于 2015-8-4 17:08 编辑
cddx 发表于 2015-8-4 13:37
面向对象三大特性;封装、继承、多态。C能完成多少呢?能容易优雅地完成吗?如果只能用C笨拙完成部分工作, ...


一针见血!

目前的确写的笨拙,也欢迎你讲出你用OO心得。

我学完初中 speak in english.和华莱士 speak in english比.肯定是有区别的。只要达到了沟通的目的,有什么不可以?

另外,用C的一个原因,是代码的复用。做嵌入式这块的开发,考虑到代码还会由别人来重复调用,选C更明智。


出0入0汤圆

发表于 2016-5-4 09:38:08 | 显示全部楼层
独孤帅 发表于 2015-3-26 09:56
由Stepmotor.c文件中的其他函数来维护这个结构体
例如:
/* 各轴属性初始化 */

我把你的代码复制到text文档,整理好慢慢看和分析。看完很有启发。
定义成对象之后,每次操作都把对象指针送到函数里面,以对象作为整体来处理。
不同的对象传不同的指针。然后外面的控制函数只有一个,它接收不同的对象进行
处理,来实现不同的目的。这种做法比平常做法要占用更多RAM。但是为了程序易读
,条理清晰,好维护,很值得这样做。
接下来,我准备应用一下。感谢分享。

出0入0汤圆

发表于 2016-5-4 09:45:23 | 显示全部楼层
独孤帅 发表于 2015-3-26 18:44
为了方便理解,我选择使用简单的拓扑结构:文件分层,每个文件只能访问比它低一级的文件,同层之间不能互 ...

这里你说的,我暂时还没能体会。分成四层之后,确实很方便移植。但是要自己划分层次,
估计需要一定的经验积累。

出0入0汤圆

发表于 2016-5-4 10:24:45 | 显示全部楼层
单片机用c++,  一个多态,几个虚函数,程序存储空间就满了。。。
单片机老老实实的用C吧。

出0入0汤圆

发表于 2016-5-4 11:05:36 | 显示全部楼层
独孤帅 发表于 2015-3-26 18:44
为了方便理解,我选择使用简单的拓扑结构:文件分层,每个文件只能访问比它低一级的文件,同层之间不能互 ...

看明白了,这样确实分的好清晰。

出0入0汤圆

发表于 2016-5-4 12:36:18 | 显示全部楼层
结构体加条件编译加分层设计,已经非常方便了,

出0入0汤圆

发表于 2016-5-4 12:56:43 | 显示全部楼层
面向对象是一种思想

出0入0汤圆

发表于 2016-5-4 16:32:04 | 显示全部楼层
又是一种高大上的应用思路!

出0入0汤圆

发表于 2016-5-4 16:42:12 | 显示全部楼层
Yvan 发表于 2016-5-4 11:05
看明白了,这样确实分的好清晰。

  欢迎来群里讨论

出0入0汤圆

发表于 2016-5-4 17:37:20 | 显示全部楼层
本帖最后由 Yvan 于 2016-5-4 17:41 编辑


..................

出0入0汤圆

发表于 2016-5-4 17:40:57 | 显示全部楼层

刚刚把函数指针加进结构体里面,实践了一下。在写的过程当中,我感觉不出这样做有什么好处?
举个例子:
有10只狗,我要喂狗。定义一个结构体如下:
typedef struct {
        名字;
        体重;
        身高;
        喂狗();
}Dog;

Dog  DogData[10];

喂狗操作:
DogData[0].喂狗();
DogData[1].喂狗();
DogData[2].喂狗();
DogData[3].喂狗();
DogData[4].喂狗();
...

另外,也可以这样写、实现喂狗:
去掉结构体的函数指针。
void 喂狗(Dog  *pDogdata){
//实现喂狗操作
}

喂狗操作:
喂狗(&DogData[0]);
喂狗(&DogData[1]);
喂狗(&DogData[2]);
喂狗(&DogData[3]);
...

谁能讲解一下,好处在哪里?

出0入0汤圆

发表于 2016-5-4 20:45:24 | 显示全部楼层
Yvan 发表于 2016-5-4 17:40
刚刚把函数指针加进结构体里面,实践了一下。在写的过程当中,我感觉不出这样做有什么好处?
举个例子:
...

这个写法主要目的是减少层间耦合产生的代码复杂度,一般来说,结构体里的回调函数越多,结构体对应的实际对象越多,就越有意义

当函数可以通过表驱动或者数据驱动解决复杂度问题时,层主写的这个应用确实可用可不用,用了只是代码风格更好,复杂度却不会减少,因为它本身就是常数的
解释一下:表驱动就是用表(或者说数组,堆栈之类的)的索引数(或者偏移地址之类的)可以改变函数的走向(比如说有12个结构体,给个参数就能表示执行哪个结构体的函数,原理类似虚函数,但是比虚函数更灵活)
数据驱动就是用一个或几个数据就可以改变函数的走向,原理同上
分别对应层主说的第二种和第一种实现

但是层主有没有想过,有的实现是不能用这两种驱动简单改变走向的,例如:
GpioCtrlReg1.xxx
GpioCtrlReg2.xxx
GpioCtrlReg3.xxx
GpioCtrlReg4.xxx
可以发现,虽然这些寄存器定义的结构体名称是按数字顺序排列的,但是我们访问的时候却不能简单的用一个数字来访问,数字也算是名字(名字在代码里是个字符串,编译后就只是个地址了)的一部分
所以,为了实现通过一个数字来决定去哪个方向,我们需要一个if else组合或一个switch case语句
switch (RegNum)
{
        case 1 : /* 访问 GpioCtrlReg1.xxx */ break;
        case 2 : /* 访问 GpioCtrlReg2.xxx */ break;
        case 3 : /* 访问 GpioCtrlReg3.xxx */ break;
        case 4 : /* 访问 GpioCtrlReg4.xxx */ break;
        default : break;
}
把它放在一个函数里,就做成了一个简单的封装,但是这样我们的工作量是与分支的数量成正比的,如果我们有很多个区域要访问,每个区域有很多分支,这个工作会很累(开发复杂度),而且不方便维护(维护复杂度)
在现代的基于虚拟机的语言中,已经有了解决办法,那就是反射。基于虚拟机的语言中,变量名,函数名,类名都是字符串,而不是地址码,这样我们就可以通过字符串匹配来实现封装(不需要分支)
上面说的是“光明大道”,我们用C语言的回调函数也可以走一些“小路”来解决这个问题
这个方法就是:将指向这些函数、回调函数、变量的指针(分别对应函数指针、二重函数指针、变量指针)封装在结构体里,具体应该走向哪里,在初始化时就固定下来(赋初值,相当于构造函数),
这样用数字来访问结构体,就可以实现封装。这样的工作量即与分支数量的二次方根成正比

出0入0汤圆

发表于 2016-5-4 21:01:27 | 显示全部楼层
多有结构体,写得多了就有感觉了,哈哈

出0入0汤圆

发表于 2016-5-4 23:55:04 | 显示全部楼层
独孤帅 发表于 2016-5-4 20:45
这个写法主要目的是减少层间耦合产生的代码复杂度,一般来说,结构体里的回调函数越多,结构体对应的实际 ...

感谢讲解!
这样的话,我目前还真没办法去很好体验这种面向对象带来的好处,因为暂时还没遇到
这样的任务需求。现在我就是想像你写步进电机轴控制那部分程序一样,分层,模块化
清晰,好维护。我现在在改别人的代码,那个代码定义了很多全局变量,我现在也只是
把那些散的全局变量组合到结构体当中,用结构体的形式来访问。只是改变形态,未动
筋骨。然而我却想用面向对象地方法去改,做了很多工作之后才发现似乎意义不大。故
此有此一问。

出0入4汤圆

发表于 2016-6-9 09:36:38 | 显示全部楼层
zhyong319 发表于 2015-3-14 21:17
正解。
用C++就可以了,面向对象C++还要便利得多。

C++占用空间不是比C大吗

出0入0汤圆

发表于 2016-6-19 18:01:42 | 显示全部楼层
函数可以放在结构体内,把结构体想象成c++的类
一般地,函数名或函数指针放入结构体内注册回调函数
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-10-2 21:57

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表