動態

詳情 返回 返回

C語言一點五編程實戰:純 C 的模塊化×繼承×多態框架 - 動態 詳情

本文將大量涉及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)的形式,而且不需要管類的具體函數實現。這裏我們將指向類的一級指針強制類型轉換為指向接口的二級指針,再解引用就得到了一個僅指向接口的一級指針,再用成員訪問符使用接口函數。

這個過程中,要將指向類的一級指針強制類型轉換為指向接口的二級指針,就需要類的起始地址與接口的起始地址相同,也就是為什麼上面説方法集的指針一定要放在類結構體的第一個,這樣指向接口的二級指針解引用後才會指向接口。
image

聲明方法實例

上面定義了抽象的接口和類,該到這個接口函數的具體實現了,當然,還要寫上類初始化函數的聲明:

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
image

關於我們更多介紹可以查看雲文檔:Freak嵌入式工作室雲文檔,或者訪問我們的wiki:https://github.com/leezisheng/Doc/wik

image

Add a new 評論

Some HTML is okay.