【杀手锏】用“万能C编程”来引出"面向对象"
1、聊一聊
最近文章更新的频度稍微有点慢,不是因为作者懒,而是因为确实很忙,工作,生活上都是各种各样的事情!好了,同样开头为大家推荐一首《感谢你曾来过》,感谢默默支持作者的小伙伴们!
好了,今天为大家带来在C设计中引入面向对象思想来进行编程。
2、"高内聚、低耦合"
大家在谈到面向对象编程的时候基本都是讨论软件的“高内聚、低耦合”的特点,这6个字也是算是成为了大部分评判代码质量的一个标准,所以很多小伙伴一开始学习编程就会问道:"如何实现代码的高内聚、低耦合"?我只想说这个问题还真不是三言两句就能说清楚的,需要开发人员有一定的代码量和软件设计方面的开发实战经验。
不过既然提到了,作者就在这里谈谈自己的一些认知和看法,在讨论一些问题的时候首先我们需要有设置一定的前提条件,否则一味的谈论是没有丝毫意义的,所以在分析前我们需要确定一个维度来看待问题,高内聚需要内部代码和功能形成较强的关联,而低耦合却是要降低模块间的内部依赖。
打个比方: 从人类角度看,每个人类都是一个单一的个体,其眼睛、鼻子、嘴巴等等形成了一个高度的内聚。而人与人之间又相互独立,形成了一个较低的耦合。然而我们如果换一个角度来看来人类,那么人与人之间或许又形成了一个较强的内聚,人与其他动物之间又形成了较低的耦合,所以进一步类比代码设计也就是一样的道理了,面向对象其实也就为了实现这个目标而产生的,所以这里我们用万能C来看看如何引入该思想:
3、用面向对象来玩C编程
对于封装性,看过我的一些文章小伙伴应该有一个比较清晰的认识了吧,特别是讲解结构体的时候,其中有个非常精妙的图不知道大家还是否记得,如果不记得可以进去先看看,<【典藏】大佬们都在用的结构体进阶小技巧>,结构体就是数据的集合,数据就是一个对象的属性和方法,把共用的接口给外部使用,把私用的进行隐蔽,这样就形成了良好的封装,下面直接上最简单的理解代码:
示例代码:
1#include <stdio.h>
2#include <stdlib.h>
3/**************************************
4 * Fuction: 仿类
5 * Author :(公众号:最后一个bug)
6 **************************************/
7typedef struct _tag_Class
8{
9 int Val;
10 void (*pmethod)(struct _tag_Class* pData);
11}sClass;
12
13/**************************************
14 * Fuction: 方法
15 * Author :(公众号:最后一个bug)
16 **************************************/
17void printfVal(sClass* pObject)
18{
19
20 printf("Object:%d\n",pObject->Val);
21}
22/**************************************
23 * Fuction: main
24 * Author :(公众号:最后一个bug)
25 **************************************/
26int main(int argc, char *argv[]) {
27 //定义两个对象
28 sClass stObject1;
29 sClass stObject2;
30 //初始化对象属性
31 stObject1.Val = 1;
32 stObject2.Val = 2;
33 stObject1.pmethod = printfVal;
34 stObject2.pmethod = printfVal;
35 //进行对象的使用
36 stObject1.pmethod(&stObject1);
37 stObject2.pmethod(&stObject2);
38
39 printf("欢迎关注公众号:最后一个bug\n");
40 return 0;
41}
运行结果:
对于继承性,就是父母有的我都有,并且还会有自己独特的地方(数据),对于代码里面无非就是属性和方法了,结合结构体地址总是指向其结构体首地址的特点,利用父子结构体前面字段相同进行直接地址传递以后强制类型转化便可以进行访问,不过这仅仅只是单继承,对于多继承问题作者暂时不展开。
示例代码:
1#include <stdio.h>
2#include <stdlib.h>
3 /**************************************
4 * Fuction: 仿父类
5 * Author :(公众号:最后一个bug)
6 **************************************/
7 typedef struct _tag_Parent
8 {
9 int ParentVal; //父类属性
10 void (*pmethod)(struct _tag_Parent* pData);//父类方法
11}sParent;
12
13/**************************************
14 * Fuction: 仿子类
15 * Author :(公众号:最后一个bug)
16**************************************/
17typedef struct _tag_Child
18{
19 sParent ParentObj;//继承父类数据和方法
20 int ChildVal;//子类特殊属性
21 void (*pmethod)(struct _tag_Child* pData);//子类特殊方法
22}sChild;
23
24/**************************************
25 * Fuction: 父类方法
26 * Author :(公众号:最后一个bug)
27 **************************************/
28void ParentprintfVal(sParent* pObject)
29{
30
31 printf("Parent Object:%d\n",pObject->ParentVal);
32}
33
34/**************************************
35 * Fuction: 子类方法
36 * Author :(公众号:最后一个bug)
37 **************************************/
38void ChildprintfVal(sChild* pObject)
39{
40
41 printf("Child Object:%d\n",pObject->ChildVal);
42}
43
44/**************************************
45 * Fuction: 对接父类的公共接口
46 * Author :(公众号:最后一个bug)
47 **************************************/
48void printfVal(sParent* pObject)
49{
50 pObject->pmethod(pObject);
51}
52
53/**************************************
54 * Fuction: main
55 * Author :(公众号:最后一个bug)
56 **************************************/
57int main(int argc, char *argv[]) {
58 //定义父类对象
59 sParent stParentObj;
60 sChild stChildObj;
61
62 //构造(初始化对象和方法)
63 stParentObj.ParentVal = 10;
64 stParentObj.pmethod = ParentprintfVal;
65
66 //初始化子类对象和方法
67 stChildObj.ParentObj.ParentVal = 20;
68 stChildObj.ParentObj.pmethod = ParentprintfVal;
69 stChildObj.ChildVal = 11;
70 stChildObj.pmethod = ChildprintfVal;
71
72 //调用公共的外部接口
73 printfVal((sParent*)&stParentObj);
74 printfVal((sParent*)&stChildObj);
75
76 printf("欢迎关注公众号:最后一个bug\n");
77 return 0;
78}
运行结果:
多态简单一点说就是一个接口可以产生多种状态处理,上一节其实也说明了这个特性,不过C++中的多态来自编译的多态和运行时的多态,比如重载包括类重载和函数重载属于编译多态、虚函数等等属于运行时多态,如果完全用C来模拟其实现还是相对比较复杂的,不过我们的目的仅仅只是说引入面向对象的一些思路来指导C编程,这里对于C编程就简单一点实现函数重载,在C中通过传递参数就能够实现一个函数的多态,不过这个传参可能需要更加灵活一点,上代码:
示例代码:
1#include <stdio.h>
2#include <stdlib.h>
3/**************************************
4 * Fuction: 任意数据结构
5 * Author :(公众号:最后一个bug)
6 **************************************/
7typedef struct _tag_Data
8{
9 void* pdata;
10 void (*method)(struct _tag_Data* pData);
11}sData;
12
13/**************************************
14 * Fuction: 具体实现1
15 * Author :(公众号:最后一个bug)
16 **************************************/
17void Add(sData *pData)
18{
19 int * Data = (int *)pData->pdata;
20 printf("Add = %d\n",(Data[0] + Data[1]));
21}
22
23/**************************************
24 * Fuction: 具体实现2
25 * Author :(公众号:最后一个bug)
26 **************************************/
27void Sub(sData *pData)
28{
29 int * Data = (int *)pData->pdata;
30
31 printf("Sub = %d\n",(Data[0] - Data[1]));
32}
33
34/**************************************
35 * Fuction: 多态函数
36 * Author :(公众号:最后一个bug)
37 **************************************/
38void Cal(sData *pData)
39{
40 pData->method(pData);
41}
42
43/**************************************
44 * Fuction: main
45 * Author :(公众号:最后一个bug)
46 **************************************/
47int main(int argc, char *argv[]) {
48
49 int Array[2] ={4 ,2};
50 sData stData1;
51 sData stData2;
52
53 stData1.pdata = (void*)Array;
54 stData1.method = Add;
55
56 stData2.pdata = (void*)Array;
57 stData2.method = Sub;
58
59 Cal(&stData1);
60 Cal(&stData2);
61
62 printf("欢迎关注公众号:最后一个bug\n");
63 return 0;
64}
运行结果:
4、最后小结
今天用C引出面向对象就讲这么多吧,因为很多小伙伴没有系统学习过C++语言,可能对于面向对象的理解不是特别形象,所以作者上面也只是简单的是C模拟了面向对象编程的“冰上一角",更多实现作者还会在后期的文章进行阐述,这里就简单给大家尝个鲜。
目前也存在非常多新型的编程设计模式,最终的编程我们还是要落在解决具体的问题、满足用户需求上来,就像一个简单的设计其实根本就不需要有太多高级编程技巧的。然而对于嵌入式应用方面大部分的软件设计还并没有发展到面向对象思路无法胜任的地步。
好了,这里是公众号:“最后一个bug”,一个为大家打造的技术知识提升基地。同时非常感谢各位小伙伴的支持,我们下期精彩见!
推荐好文 点击蓝色字体即可跳转
☞【必看】嵌入式Engineer必经之路 -- "同步问题"