我是靠谱客的博主 傲娇唇膏,最近开发中收集的这篇文章主要介绍学习笔记:C语言实现面向对象的封装、继承、多态,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

转载请标明出处。

学习内容

C语言实现面向对象的封装、继承、多态。

实践实验

继承代码实现:

//未新建.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
*/

多态代码实现:

typedef 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
*/

个人总结

  1. 刚开始写继承相关时陷入了思维定式,总想着把基类放在第一个,用头指针相同的方式进行传递。写完多态后才发觉,C语言写继承的根本原理是,开一个结构体的空间(即父类),其他子类通过指针去调用、更改父类空间的内容即可实现继承,也就意味着基类不需要放在第一位,且可以实现多个基类共存。
  2. 多态的实现原理是,传递指针过程中,指针指向的内容不会发生变化,所以我们把函数指针放在结构体第一位,这样无论结构体后面的成员是什么样的,我们通过头指针就可调用函数指针指向的函数。我们定义一个void指针作为输入参数,即接口,可以传递不同类型的指针。在接口函数中将void指针强制转换为父类类型指针,调用该结构体指针成员中的函数指针,即可调用对应不同类型对应的函数(需要所有函数指针都等价为结构体的头指针)。
  3. 从多态的定义来看,上述代码中,继承代码中对虚函数的动态变化也可称为多态,多态代码中同一个接口可输入不同类型的参数也可看作是多态。C语言面向对象还是图一乐,没必要完全去套用定义,偶尔用到的时候心里有个概念就好,C++、Java才是面向对象的主战场,不管是从难度还是代码量上看都是如此。

遇到的问题

  • 指针指向的结构体一定要有完整定义,不能只定义一个结构体指针。
  • 指向基类的指针最好是在初始化的时候,用const声明并指向基类,防止编译器优化或其他Bug导致无法继承。
  • 接口函数中用于传递的结构体指针要用const声明,防止出现Bug导致指针变化。
  • C语言写面向对象更多是为了增加对指针的应用及了解,毕竟小工程用不到,大工程C++,Keil也可支持C++编译,且在C++中嵌套C。

最后

以上就是傲娇唇膏为你收集整理的学习笔记:C语言实现面向对象的封装、继承、多态的全部内容,希望文章能够帮你解决学习笔记:C语言实现面向对象的封装、继承、多态所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部