学习笔记: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_:%d\r",PlaneMsg->Msg_.bit);
}
void Plane_2(struct Msg * const PlaneMsg)
{
    printf("Plane_2:%d\r",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:%d\r\n",PlaneMsg->Msg_.data);
                break;
            default:
                PlaneMsg->vptr = &behavior.vptr2;
                printf("Msg_Init:%d\r\n",PlaneMsg->Msg_.data);
                break;
        }

    }else 
    {
        printf("Msg_Init:Error!%d\r\n",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:%d\r\n",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_Processing\r\n");
}
void B_Msg_Processing(void* const MsgData)
{
    if (NULL == MsgData)return;
    struct B_Msg_* p = (struct B_Msg_*)MsgData;
    printf("B_Msg_Processing:%d,%d\r\n", 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,%d\r\n", 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("Processing\r\n");
    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:\r\n");
    Processing(&A_Msg);
    printf("\r\nB_Msg:\r\n");
    Processing(&B_Msg);
    printf("\r\nC_Msg:\r\n");
    Processing(&C_Msg);

    printf("\r\nMain\r\n");
}

/*输出结果:
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。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值