转载请标明出处。
学习内容
复制代码
1
2C语言实现面向对象的封装、继承、多态。
实践实验
继承代码实现:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101//未新建.h文件,仅使用了.c #include "stdint.h" #include "stdio.h" #include "string.h" #include "stdlib.h" #define uint8_t unsigned int #define uint16_t unsigned long //测试相关结构体封装 struct Msg; typedef struct { uint8_t Flag1; uint8_t Flag2; }Flag_;//父类 typedef struct { uint8_t Flag;//出现逻辑外判断防止程序跑飞标志 uint8_t data; }Frame; typedef struct { void (*MsgProcessing)(struct Msg * const PlaneMsg); }MsgVptr; //虚函数 struct Msg{ Flag_ Flag; //共有的属性 MsgVptr const * vptr; //由属性决定的行为 Frame Msg_; //独立的属性 };//子类 struct behavior_{ MsgVptr vptr1; MsgVptr vptr2; }behavior;//虚函数表 //测试相关函数 void Plane_(struct Msg * const PlaneMsg) { printf("Plane_:%dr",PlaneMsg->Msg_.bit); } void Plane_2(struct Msg * const PlaneMsg) { printf("Plane_2:%dr",PlaneMsg->Msg_.bit); } void Msg_Init(struct Msg * const PlaneMsg) { PlaneMsg->Msg_.Flag = 0; if((PlaneMsg->Flag.Flag1 == 1)&&(PlaneMsg->Flag.Flag2 == 0)) { switch(PlaneMsg->Msg_.data) { case 1: PlaneMsg->vptr = &behavior.vptr1;//将对应行为函数地址传递到结构体 printf("Msg_Init:%drn",PlaneMsg->Msg_.data); break; default: PlaneMsg->vptr = &behavior.vptr2; printf("Msg_Init:%drn",PlaneMsg->Msg_.data); break; } }else { printf("Msg_Init:Error!%drn",PlaneMsg->Flag.Flag1); PlaneMsg->Msg_.Flag = 1;//防止未对虚函数指针赋值导致后面程序跑飞 } } void Msg_Processing(struct Msg * const PlaneMsg) { if(PlaneMsg->Msg_.Flag == 1)return; PlaneMsg->vptr->MsgProcessing(PlaneMsg); } void PlaneMsgProcessing_(struct Msg * const PlaneMsg) { Msg_Init(PlaneMsg);//根据成员属性更改行为函数 Msg_Processing(PlaneMsg);//执行行为函数 PlaneMsg->Flag.Flag1 = 10;//测试子类赋值父类是否会更改 } int main() { struct Msg PlaneMsg ={0};//子类 Flag_ *const Flag = (Flag_ *)&PlaneMsg;//父类 //模拟输入值 PlaneMsg.Msg_ = (Frame){0,1}; Flag->Flag1 = 1;//测试父类赋值子类是否会更改 Flag->Flag2 = 0; //初始化行为函数表 behavior= (struct behavior_){ {&Plane_}, {&Plane_2} }; PlaneMsgProcessing_(&PlaneMsg); printf("main:%drn",Flag->Flag1); return 0; } /* 输出结果为: Msg_Init:1 Plane_:1 main:10 */
多态代码实现:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88typedef struct { void (*Processing_)(void* p); }MsgVptr;//虚函数 typedef struct { uint8_t Flag1; uint8_t Flag2; }Flag_; typedef struct { uint8_t Flag; uint8_t data; }Frame; struct A_Msg_ { MsgVptr* const vptr; }; struct B_Msg_ { struct A_Msg_ Msg; Flag_ *const Flag; }; struct C_Msg_ { struct B_Msg_ Msg; Frame Msg_; }; struct behavior_ { MsgVptr vptr1; MsgVptr vptr2; MsgVptr vptr3; }behavior;//虚函数表 void A_Msg_Processing(void* const MsgData) { if (NULL == MsgData)return; struct A_Msg_* p = (struct A_Msg_*)MsgData; printf("A_Msg_Processingrn"); } void B_Msg_Processing(void* const MsgData) { if (NULL == MsgData)return; struct B_Msg_* p = (struct B_Msg_*)MsgData; printf("B_Msg_Processing:%d,%drn", p->Flag->Flag1, p->Flag->Flag2); } void C_Msg_Processing(void* const MsgData) { if (NULL == MsgData)return; struct C_Msg_* p = (struct C_Msg_*)MsgData; printf("C_Msg_Processing:%d,%drn", p->Msg.Flag->Flag1, p->Msg.Flag->Flag2); } void Processing(void* const MsgData) { if (NULL == MsgData)return; struct A_Msg_ * MsgP = (struct A_Msg_* )MsgData; printf("Processingrn"); MsgP->vptr->Processing_(MsgData); } int main() { behavior = (struct behavior_){ {&A_Msg_Processing}, {&B_Msg_Processing}, {&C_Msg_Processing} }; Flag_ Flag = {1,2}; struct A_Msg_ A_Msg = { &behavior.vptr1 }; struct B_Msg_ B_Msg = { { &behavior.vptr2 },&Flag }; struct C_Msg_ C_Msg = { &behavior.vptr3 ,&Flag }; Flag.Flag1 = 5; Flag.Flag2 = 5; printf("A_Msg:rn"); Processing(&A_Msg); printf("rnB_Msg:rn"); Processing(&B_Msg); printf("rnC_Msg:rn"); Processing(&C_Msg); printf("rnMainrn"); } /*输出结果: A_Msg: Processing A_Msg_Processing B_Msg: Processing B_Msg_Processing:5,5 C_Msg: Processing C_Msg_Processing:5,5 Main */
个人总结
- 刚开始写继承相关时陷入了思维定式,总想着把基类放在第一个,用头指针相同的方式进行传递。写完多态后才发觉,C语言写继承的根本原理是,开一个结构体的空间(即父类),其他子类通过指针去调用、更改父类空间的内容即可实现继承,也就意味着基类不需要放在第一位,且可以实现多个基类共存。
- 多态的实现原理是,传递指针过程中,指针指向的内容不会发生变化,所以我们把函数指针放在结构体第一位,这样无论结构体后面的成员是什么样的,我们通过头指针就可调用函数指针指向的函数。我们定义一个void指针作为输入参数,即接口,可以传递不同类型的指针。在接口函数中将void指针强制转换为父类类型指针,调用该结构体指针成员中的函数指针,即可调用对应不同类型对应的函数(需要所有函数指针都等价为结构体的头指针)。
- 从多态的定义来看,上述代码中,继承代码中对虚函数的动态变化也可称为多态,多态代码中同一个接口可输入不同类型的参数也可看作是多态。C语言面向对象还是图一乐,没必要完全去套用定义,偶尔用到的时候心里有个概念就好,C++、Java才是面向对象的主战场,不管是从难度还是代码量上看都是如此。
遇到的问题
- 指针指向的结构体一定要有完整定义,不能只定义一个结构体指针。
- 指向基类的指针最好是在初始化的时候,用const声明并指向基类,防止编译器优化或其他Bug导致无法继承。
- 接口函数中用于传递的结构体指针要用const声明,防止出现Bug导致指针变化。
- C语言写面向对象更多是为了增加对指针的应用及了解,毕竟小工程用不到,大工程C++,Keil也可支持C++编译,且在C++中嵌套C。
最后
以上就是傲娇唇膏最近收集整理的关于学习笔记:C语言实现面向对象的封装、继承、多态的全部内容,更多相关学习笔记:C语言实现面向对象内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复