C语言面向对象编程

Taboo 发布于 14 天前 153 次阅读


本文将大量涉及C语言高级操作,如函数指针、结构体指针、二级指针、指针频繁引用解引用、typedef、static、inline和C语言项目结构等知识,请确保自己不会被上述知识强暴,如果没有这顾虑,请尽情享受~

渊源

一开始时候,我是不知道这个技术的。在某一天我在刷B站的时候,看到一个作者为“一点五编程”的视频。他提出了一种编程思想,命名为“一点五编程”。其中:

"一"指的是模块化思想

“点五”指的是(*p)->f(p)技巧

我一看,好像是一种高端的技巧哇,于是开始看他的视频,发现讲解这一技术核心的视频全都是充电的!!!好吧,那我只好翻你文档看了,找到了他的个人博客。唔,好像什么都写了,但好像少点什么,,,哦,没教我到底怎么组织文件。

然后我继续翻网页,在CSDN上发现三篇文章,讲的是对“一点五编程”的解读。但是后来在自己实操过程中,还是发现了其中的错误。

看了这么多文章和视频,脑子一拍,这不就是面向对象的编程范式吗,只不过C语言是面向过程的语言,没有现成的面向对象的组件,但是思想上完全就是OOP那套嘛!

于是我开始自己扒,终于,也是让我扒出来了~

在文章的最后我会放上一个循迹小车的项目,当然,功能上伪实现(狗头),那接下来先讲下这门技术的基本理论和开发流程吧

理论

面向对象编程

我们先来说一下面向对象编程是什么:

面向对象编程是一种编程范式,它通过定义类和对象来组织和设计程序。

在面向对象编程中,程序猿通过创建类来定义数据结构和行为,通过创建对象来实例化这些类,并通过对象之间的交互来实现程序的功能。这种方法使得程序的结构更加清晰和易于维护。

面向对象编程有几个特性,分别是:封装、继承、多态、抽象,这里就不再说了,只要知道本文会体现就行,(纠结)因为毕竟还是挺难理解的,我也讲不明白,可以看看别的大佬的文章。

为什么要把面向对象编程拉出来说呢?众所众知,嵌入式是一个软硬件结合的学科,这就会存在一个问题,就是我们会非常在乎硬件的实现,上层的功能实现就实现了,也不会在乎开发的结构、后期的维护等,这一点在初学者身上体现的淋漓尽致。

而面向对象编程就致力于让程序更加模块化,通过继承和多态,使得大量代码复用,它还有模拟现实世界中对象和关系的能力。这样,就为开发者提供了一种自顶向下的开发思路。同时,它将上层实现与下层驱动相隔离,让更换开发平台变得简单。

流程

我将整个C语言面向对象编程的开发分为三个阶段,分别是声明、实现和使用。

声明

声明阶段又可以分为五个步骤,这些都是在头文件中写入的,分别是:

  1. 声明接口函数
  2. 定义接口结构体
  3. 定义类结构体
  4. 定义类型转换内联函数
  5. 声明方法实例

其中,声明接口函数、定义接口结构体和定义类型转换内联函数仅需书写一次,另外两个步骤可以根据实际需求定义更多的类和方法实例。

声明接口函数

在这里,接口就是类的行为方法集,控制整个类的行为方式。以循迹模块为例,读取循迹信息就是它的一个行为;以电机驱动模块为例,控制电机停转、正转、反转和控制转动速度就是它的一系列行为。我们首先要思考我们所抽象出的类有哪些行为方法,写成下面形式:

typedef int (*Method0FnT)(void* self, ...);
typedef int (*Method1FnT)(void* self, ...);
  .
  .
  .
typedef int (*MethodnFnT)(void* self, ...);

定义接口结构体

接下来,我们要将上面的接口函数放到一个接口结构体中,方便由各个类使用:

typedef struct
{
    Method0FnT method0Fn;
    Method1FnT method1Fn;
      .
      .
      .
    MethodnFT methodnFn;
} MethodsT;

定义类结构体

完成上面步骤后,一个类的方法集就总结好了,再由方法集和类的各个属性组成完整的类,这里一定要注意,方法集指针一定要放在类结构体的第一个,否则会出现错误:

typedef struct
{
    MethodsT* methods;
    Type attribute0;
    Type attribute1;
      .
      .
      .
    Type attributen;
} Class;

定义类型转换内联函数

这里是我们实现多态这一特性最核心的步骤,写成如下格式:

static inline int method0Fn(void* self, ...)
{
    return (*(MethodsT**)self)->method0Fn(self, ...);
}

static inline int method1Fn(void* self, ...)
{
    return (*(MethodsT**)self)->method1Fn(self, ...);
}

  .
  .
  .

static inline int methodnFn(void* self, ...)
{
    return (*(MethodsT**)self)->methodnFn(self, ...);
}

使用上面的语句,我们能够将(*p)->f(p)改写为f(p)的形式,而且不需要管类的具体函数实现。这里我们将指向类的一级指针强制类型转换为指向接口的二级指针,再解引用就得到了一个仅指向接口的一级指针,再用成员访问符使用接口函数。

这个过程中,要将指向类的一级指针强制类型转换为指向接口的二级指针,就需要类的起始地址与接口的起始地址相同,也就是为什么上面说方法集的指针一定要放在类结构体的第一个,这样指向接口的二级指针解引用后才会指向接口。

声明方法实例

上面定义了抽象的接口和类,该到这个接口函数的具体实现了,当然,还要写上类初始化函数的声明:

int classMethod0(void* self, ...);
int classMethod1(void* self, ...);
  .
  .
  .
int classMethodn(void* self, ...);

int classInit(void* self, ...);

实现

头文件中内容就完成了,下面是具体的相关函数的实现了,下面部分都在源文件中写入,分为三个步骤:

  1. 定义方法实例
  2. 定义接口实例
  3. 定义类初始化函数

三个步骤的内容均由头文件中的声明限制。

定义方法实例

方法的实例我们已经在头文件中声明过了,在这里我们进行这些方法实例的定义:

int classMethod0(void* self, ...);
{
    Class* pClass= (Class*)self;

    //具体内容实现
    //异常处理

    return 1;
}

int classMethod1(void* self, ...)
{
    Class* pClass= (Class*)self;
    ...
    return 1;
}

  .
  .
  .

int classMethodn(void* self, ...)
{
    Class* pClass= (Class*)self;
    ...
    return 1;
}

定义接口实例

具体的方法已经有了,接下来我们要实现具体的接口了,将方法实例的函数指针传入到接口结构体中:

static MethodsT classMethods=
{
    .method0Fn= classMethod0,
    .method1Fn= classMethod1,
      .
      .
      .
    .methodnFn= classMethodn
}

定义类的初始化函数

最后我们编写所需类的初始化的函数,类的属性值将通过初始化函数传入:

int classInit(void* self, ...)
{
    Class* pClass= (Class*)self;

    pClass->methods= &classMethods;
    pClass->attribute0= ...;
    pClass->attribute1= ...;
      .
      .
      .
    pClass->attributen= ...;

    //其他初始化内容
    //异常处理

    return 1;
}

使用

使用起来就简单了,首先我们要生成类的实例,然后使用初始化函数进行类初始化,然后,使用!

//生成实例可以放在main.h中或者主函数前或者主函数开头
Class class
//初始化要放在开头
classInit(&class, ...);
//使用方法就放在任何你需要其执行的地方即可
method0Fn(&class, ...);
method1Fn(&class, ...);
  .
  .
  .
methodnFn(&class, ...);

就这样,我们的所有功能就实现啦,我相信你一定学会了!(狗头)

附录

循迹小车的伪实现,会体现出上面没有提及的继承特性

trace.h

#ifndef _TRACE_H
#define _TRACE_H

typedef int (*TraceReadFnT)(void* self, int* result);


typedef struct
{
    TraceReadFnT traceReadFn;
} TraceMethodsT;


typedef struct
{
    TraceMethodsT* pTraceMethods;
    int tracePin;
} Trace;

static inline int traceReadFn(void* self, int* result)
{
    return (*(TraceMethodsT**)self)->traceReadFn(self, result);
}

int traceRead(void* self, int* result);
int traceInit(void* self, int pin);

#endif

trace.c

#include <stdlib.h>
#include <stdio.h>

#include "trace.h"

int traceRead(void* self, int* result)
{
    Trace* pTrace= (Trace*)self;

    int num= rand()%2;
    if(num!=0&&num!=1)
        return 0;

    *result=num;
    printf("trace result is %d\n",*result);
    return 1;
}

static TraceMethodsT TraceMethods=
{
    .traceReadFn= (TraceReadFnT)traceRead
};

int traceInit(void* self, int pin)
{
    Trace* pTrace= (Trace*)self;

    pTrace->pTraceMethods= &TraceMethods;
    pTrace->tracePin= pin;

    printf("Trace object inited successfully\n");
    return 1;
}

motor.h

#ifndef _MOTOR_H
#define _MOTOR_H

typedef enum
{
    MOTOR_STOP,
    MOTOR_FORWARD,
    MOTOR_REVERSE
} MotorStat;


typedef int (*MotorStopFnT)(void* self);
typedef int (*MotorForwardFnT)(void* self, int speed);
typedef int (*MotorReverseFnT)(void* self, int speed);
typedef int (*MotorControlSpeedFnT)(void* self, int speed);


typedef struct
{
    MotorStopFnT motorStopFn;
    MotorForwardFnT motorForwardFn;
    MotorReverseFnT motorReverseFn;
    MotorControlSpeedFnT motorControlSpeedFn;
} MotorMethodsT;


typedef struct
{
    MotorMethodsT* pMotorMethods;
    int motorPinA;
    int motorPinB;
    int motorPinControlSpeed;
    MotorStat motorStat;
    int motorSpeed;
} Motor;


static inline int motorStopFn(void* self)
{
    return (*(MotorMethodsT**)self)->motorStopFn(self);
}

static inline int motorForwardFn(void* self, int speed)
{
    return (*(MotorMethodsT**)self)->motorForwardFn(self, speed);
}

static inline int motorReverseFn(void* self, int speed)
{
    return (*(MotorMethodsT**)self)->motorReverseFn(self, speed);
}

static inline int motorControlSpeedFn(void* self, int speed)
{
    return (*(MotorMethodsT**)self)->motorControlSpeedFn(self, speed);
}


int motorStop(void* self);
int motorForward(void* self, int speed);
int motorReverse(void* self, int speed);
int motorControlSpeed(void* self, int speed);
int motorInit(void* self, int pinA, int pinB, int pinControlSpeed);

#endif

motor.c

#include <stdio.h>

#include "motor.h"

int motorStop(void* self)
{
    Motor* pMotor= (Motor*)self;

    pMotor->motorStat= MOTOR_STOP;
    pMotor->motorSpeed= 0;

    printf("motor stopped\n");
    return 1;
}

int motorForward(void* self, int speed)
{
    Motor* pMotor= (Motor*)self;

    pMotor->motorStat= MOTOR_FORWARD;
    pMotor->motorSpeed= speed;

    printf("motor turned forward, speed is %d\n",pMotor->motorSpeed);
    return 1;
}

int motorReverse(void* self, int speed)
{
    Motor* pMotor= (Motor*)self;

    pMotor->motorStat= MOTOR_REVERSE;
    pMotor->motorSpeed= speed;

    printf("motor turned reverse, speed is %d\n",pMotor->motorSpeed);
    return 1;
}

int motorControlSpeed(void* self, int speed)
{
    Motor* pMotor= (Motor*)self;

    pMotor->motorSpeed= speed;

    printf("motor speed is turned to %d\n",pMotor->motorSpeed);
    return 1;
}

static MotorMethodsT MotorMethods=
{
    .motorStopFn= (MotorStopFnT)motorStop,
    .motorForwardFn= (MotorForwardFnT)motorForward,
    .motorReverseFn= (MotorReverseFnT)motorReverse,
    .motorControlSpeedFn= (MotorControlSpeedFnT)motorControlSpeed
};

int motorInit(void* self, int pinA, int pinB, int pinControlSpeed)
{
    Motor* pMotor= (Motor*)self;

    pMotor->pMotorMethods= &MotorMethods;
    pMotor->motorPinA= pinA;
    pMotor->motorPinB= pinB;
    pMotor->motorPinControlSpeed= pinControlSpeed;

    pMotor->motorStat= MOTOR_STOP;
    pMotor->motorSpeed= 0;

    printf("motor object inited successfully\n");
    return 1;
}

trail_car.h

#ifndef _TRAIL_CAR_H
#define _TRAIL_CAR_H

#include "trace.h"
#include "motor.h"

enum TrailCarStat
{
    CAR_STOP,
    CAR_RUN
};


typedef int (*TrailCarRunFnT)(void* self, int speedStraight, int speedTurnFast, int speedTurnLow);
typedef int (*TrailCarStopFnT)(void* self);


typedef struct
{
    TrailCarRunFnT trailCarRunFn;
    TrailCarStopFnT trailCarStopFn;
} TrailCarMethodsT;


typedef struct
{
    TrailCarMethodsT* pTrailCarMethods;
    Trace* trailCarTrace0;
    Trace* trailCarTrace1;
    Trace* trailCarTrace2;
    Trace* trailCarTrace3;
    Trace* trailCarTrace4;
    Motor* trailCarMotorLeft;
    Motor* trailCarMotorRight;
    enum TrailCarStat trailCarStat;
} TrailCar;


static inline int trailCarRunFn(void* self, int speedStraight, int speedTurnFast, int speedTurnLow)
{
    return (*(TrailCarMethodsT**)self)->trailCarRunFn(self, speedStraight, speedTurnFast, speedTurnLow);
}

static inline int trailCarStopFn(void* self)
{
    return (*(TrailCarMethodsT**)self)->trailCarStopFn(self);
}


int trailCarRun(void* self, int speedStraight, int speedTurnFast, int speedTurnLow);
int trailCarStop(void* self);
int trailCarInit
(
    void* self,
    Trace* trace0,
    Trace* trace1,
    Trace* trace2,
    Trace* trace3,
    Trace* trace4,
    Motor* motorLeft,
    Motor* motorRight
);

#endif

trail_car.c

#include <stdio.h>

#include "trace.h"
#include "motor.h"
#include "trail_car.h"

int trailCarRun(void* self, int speedStraight, int speedTurnFast, int speedTurnLow)
{
    TrailCar* pTrailCar= (TrailCar*)self;

    pTrailCar->trailCarStat= CAR_RUN;

    int result0, result1, result2, result3, result4;
    traceReadFn(pTrailCar->trailCarTrace0, &result0);
    traceReadFn(pTrailCar->trailCarTrace1, &result1);
    traceReadFn(pTrailCar->trailCarTrace2, &result2);
    traceReadFn(pTrailCar->trailCarTrace3, &result3);
    traceReadFn(pTrailCar->trailCarTrace4, &result4);

    if(result0==1&&result1==1&&result2==1&&result3==1&&result4==1)
    {
        motorStopFn(pTrailCar->trailCarMotorLeft);
        motorStopFn(pTrailCar->trailCarMotorRight);
        printf("trail car stopped\n");
    }
    else if(result0==0&&result1==0&&result2==1&&result3==0&&result4==0)
    {
        motorForwardFn(pTrailCar->trailCarMotorLeft, speedStraight);
        motorForwardFn(pTrailCar->trailCarMotorRight, speedStraight);
        printf("trail car went straight\n");
    }
    else if(result0==0&&result1==1&&result2==0&&result3==0&&result4==0)
    {
        motorForwardFn(pTrailCar->trailCarMotorLeft, speedTurnLow);
        motorForwardFn(pTrailCar->trailCarMotorRight, speedTurnFast);
        printf("trail car turned left\n");
    }
    else if(result0==0&&result1==0&&result2==0&&result3==1&&result4==0)
    {
        motorForwardFn((void*)(pTrailCar->trailCarMotorLeft), speedTurnFast);
        motorForwardFn((void*)(pTrailCar->trailCarMotorRight), speedTurnLow);
        printf("trail car turned right\n");
    }
    else
    {
        motorForwardFn(pTrailCar->trailCarMotorLeft, speedStraight);
        motorForwardFn(pTrailCar->trailCarMotorRight, speedStraight);
        printf("trail car went straight\n");
    }

    return 1;
}

int trailCarStop(void* self)
{
    TrailCar* pTrailCar= (TrailCar*)self;

    pTrailCar->trailCarStat= CAR_RUN;

    motorStopFn(pTrailCar->trailCarMotorLeft);
    motorStopFn(pTrailCar->trailCarMotorRight);

    printf("trail car stopped\n");
    return 1;
}

static TrailCarMethodsT TraceMethods=
{
    .trailCarRunFn= (TrailCarRunFnT)trailCarRun,
    .trailCarStopFn= (TrailCarStopFnT)trailCarStop
};

int trailCarInit
(
    void* self,
    Trace* trace0,
    Trace* trace1,
    Trace* trace2,
    Trace* trace3,
    Trace* trace4,
    Motor* motorLeft,
    Motor* motorRight
)
{
    TrailCar* pTrailCar= (TrailCar*)self;

    pTrailCar->pTrailCarMethods= &TraceMethods;
    pTrailCar->trailCarTrace0= trace0;
    pTrailCar->trailCarTrace1= trace1;
    pTrailCar->trailCarTrace2= trace2;
    pTrailCar->trailCarTrace3= trace3;
    pTrailCar->trailCarTrace4= trace4;
    pTrailCar->trailCarMotorLeft= motorLeft;
    pTrailCar->trailCarMotorRight= motorRight;

    // trailCarStopFn(self);
    // pTrailCar->pTrailCarMethods.trailCarStopFn(pTrailCar);
    motorStopFn(pTrailCar->trailCarMotorLeft);
    motorStopFn(pTrailCar->trailCarMotorRight);
    pTrailCar->trailCarStat= CAR_STOP;

    return 1;
}

main.h

#ifndef _MAIN_H
#define _MAIN_H

#include "trace.h"
#include "motor.h"
#include "trail_car.h"

Trace trace0;
Trace trace1;
Trace trace2;
Trace trace3;
Trace trace4;
Motor motorLeft;
Motor motorRight;

TrailCar trailCar;

#endif

main.c

#include <stdlib.h>
#include <stdio.h>

#include "trace.h"
#include "motor.h"
#include "trail_car.h"
#include "main.h"


int main()
{
    traceInit(&trace0,0);
    traceInit(&trace1,1);
    traceInit(&trace2,2);
    traceInit(&trace3,3);
    traceInit(&trace4,4);

    motorInit(&motorLeft,5,6,7);
    motorInit(&motorRight,8,9,10);
    trailCarInit
    (
        (void*)&trailCar,
        &trace0,
        &trace1,
        &trace2,
        &trace3,
        &trace4,
        &motorLeft,
        &motorRight
    );

    trailCarRunFn(&trailCar,100,100,100);
    trailCarStopFn(&trailCar);

    return 0;
}
此作者没有提供个人介绍
最后更新于 2025-05-05