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

摘要:

本文探讨在C语言中模拟面向对象编程(OOP)的"一点五编程"技术,通过函数指针、结构体嵌套和二级指针强制转换实现类、接口与多态。开发流程分声明(接口/类结构体、类型转换函数)、实现(方法绑定、初始化)和使用三阶段,强调方法集指针必须位于类结构体首地址以实现动态绑定。该方法将硬件驱动与业务逻辑解耦,结合嵌入式场景展示模块化设计,附伪实现循迹小车项目验证继承特性,为C语言赋予OOP的封装性、扩展性,提升嵌入式代码可维护性。

渊源

一开始时候,我是不知道这个技术的。在某一天我在刷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, ...);

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

附录

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

https://github.com/swfeiyu/coop

关于我们更多介绍可以查看云文档:Freak嵌入式工作室云文档,或者访问我们的wiki:https://github.com/leezisheng/Doc/wik

C语言一点五编程实战:纯 C 的模块化×继承×多态框架的更多相关文章

  1. Python源代码 -- C语言实现面向对象编程(基类&派生类&多态)

    背景 python是面向对象的解释性语言.然而python是通过C语言实现的,C语言怎么跟面向对象扯上了关系? C语言能够实现面向对象的性质? 原文链接:http://blog.csdn.net/or ...

  2. 《Go并发编程实战》读书笔记-初识Go语言

    <Go并发编程实战>读书笔记-初识Go语言 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在讲解怎样用Go语言之前,我们先介绍Go语言的特性,基础概念和标准命令. 一. ...

  3. 使用C语言调用mysql数据库编程实战以及技巧

    今天编写使用C语言调用mysql数据库编程实战以及技巧.为其它IT同行作为參考,当然有错误能够留言,共同学习. 一.mysql数据库的C语言经常使用接口API 1.首先当然是链接数据库mysql_re ...

  4. Java多线程编程实战指南(核心篇)读书笔记(五)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  5. 手把手和你一起实现一个Web框架实战——EzWeb框架(五)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(五)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 本篇代码,请选择demo5 中间件实现 ...

  6. 一步步分析:C语言如何面向对象编程

    这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...

  7. Linux下的C编程实战

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来, Linu ...

  8. Linux下的编程实战【转】

    一篇比较不错的文章, 降到了 makefile make , gcc编译器,GDB调试器, Linux文件系统,Linux文件API,.C语言库函数(C库函数的文件操作实际上是独立于具体的操作系统平台 ...

  9. 《Java多线程编程实战指南(核心篇)》阅读笔记

    <Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...

  10. 【算法】C语言趣味程序设计编程百例精解

    C语言趣味程序设计编程百例精解 C/C++语言经典.实用.趣味程序设计编程百例精解(1)  https://wenku.baidu.com/view/b9f683c08bd63186bcebbc3c. ...

随机推荐

  1. ABC323E Playlist 题解

    考虑第 \(i\) 时刻时,第 \(j\) 首歌刚好结束与第 \(i-j\) 时刻有关,因此设 \(dp_{i,j}\) 表示第 \(i\) 时刻第 \(j\) 首歌刚好结束的概率,那么 \(dp\) ...

  2. Python - [01] 简介

    人生苦短,我用Python 一.Python 是什么 Python是一个高层次的结合了解释型.编译型.互动性和面向对象的脚本语言. Python的设计具有很强的可读性,相比其他语言经常使用英文关键字, ...

  3. 机器学习 | 强化学习(6) | 策略梯度方法(Policy Gradient Method)

    6-策略梯度方法(Policy Gradient Method) 策略梯度概论(Introduction) 基于策略(Policy-Based) 的强化学习 对于上一节课(价值函数拟合)中采用参数\( ...

  4. 【ABAQUS 二次开发笔记】使用keyword 、python和matlab一起处理Odb数据

    用conversion shell element (S4R单元)建模层合板,有6层ply,每个lamina(ply)有3个 integration point,共计18个integration po ...

  5. phpstorm、goland常用快捷键

    1) 文件操作相关的快捷键 快捷键 作用 Ctrl + E 打开最近浏览过的文件 Ctrl + N 快速打开某个 struct 结构体所在的文件 Ctrl + Shift + N 快速打开文件 Shi ...

  6. selenium自动化测试+OCR-获取图片页面小说

    随着爬虫技术的发展,反爬虫技术也越来越高. 目前有些网站通过自定义字体库的方式实现反爬,主要表现在页面数据显示正常,但是页面获取到的实际数据是别的字符或者是一个编码.这种反爬需要解析网站自己的字体库, ...

  7. 张高兴的大模型开发实战:(二)使用 LangChain 构建本地知识库应用

    目录 基础概念 什么是 LangChain 什么是 Ollama 环境搭建与配置 安装 Ollama 安装 LangChain 文档加载 加载 JSON 数据 加载文件夹中的文档 文本向量化 实现问答 ...

  8. 痞子衡嵌入式:记录为i.MXRT1060更换较大容量Flash(IS25LP064A_IS25LP128F)导致二级App异常启动问题解决全过程(上篇)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是为i.MXRT1060更换较大容量Flash导致二级App异常启动问题. 痞子衡最近在支持一个 RT1062 国外客户项目,客户在项目预 ...

  9. dxTabbedMDIManager1关闭窗体

    procedure TfrmJianKongXinXi.FormClose(Sender: TObject; var Action: TCloseAction);begin Action:=caFre ...

  10. ASP.NET 自定义DataTable数据

    using System.Data; //DataTable try {     DataTable dt = new DataTable();     dt.Columns.Add("Bu ...