大部分使用 C 語言進(jìn)行開發(fā)的工程師,在接觸更高級的編程語言之前,都認(rèn)為 C 語言是面向過程的。
事實(shí)也是如此,對于一些小規(guī)模的單片機(jī)應(yīng)用程序,一般都是使用“面向過程”的思維進(jìn)行單片機(jī)C語言編程開發(fā)。
但是,如果是需要用C語言開發(fā)一些規(guī)模比較大的軟件的時候,比如操作系統(tǒng)內(nèi)核,文件系統(tǒng)底層,數(shù)據(jù)庫底層,等等,這個時候,就需要用面向?qū)ο蟮乃枷肴タ紤]和設(shè)計(jì)整個軟件框架了。
嵌入式Linux的內(nèi)核,雖然是使用 C 語言編寫的,但里面的設(shè)計(jì)大部分都使用了面向?qū)ο蟮木幊趟枷搿?/p>
很多單片機(jī)工程師或者嵌入式Linux驅(qū)動初學(xué)者,有時候會覺得驅(qū)動入門特別困難,很大一部分原因是,他們會用“過程式思維”去嘗試學(xué)習(xí)驅(qū)動框架和內(nèi)核框架,而非從“整體對象”的思維方向出發(fā),這樣容易導(dǎo)致水土不服。
任何編程語言只是一種工具,而編程思想是指導(dǎo)我們用好這個工具的關(guān)鍵。
C 語言只是工具,而面向?qū)ο笫且环N編程思想,用來指導(dǎo)我們?nèi)绾斡脧牧硪环N思維模式去使用 C 語言。
值得注意的是,并不是所有使用C語言開發(fā)的項(xiàng)目,都必須以“面向?qū)ο蟆弊鳛橹笇?dǎo),有時候“面向過程”也不一定是壞事,需要根據(jù)實(shí)際項(xiàng)目情況,具體問題具體分析。
接下來,我們將嘗試使用 C 語言進(jìn)行面向?qū)ο蟪绦蜷_發(fā),務(wù)求使用 C 語言實(shí)現(xiàn)面向?qū)ο蟮囊恍┗咎匦?,先來說說封裝。
封裝就是把一個抽象事物的屬性和屬性的操作函數(shù)打包在一起,外界的模塊只能通過這個抽象事物對外提供的函數(shù)接口,對其屬性進(jìn)行訪問。
在C++或其他高級語言中,封裝通常被稱作“類”。而 C 語言一般使用結(jié)構(gòu)體對事物進(jìn)行封裝。
說人話就是,封裝,即“封閉包裝起來”,俗稱“打包”。把事物里面一些相近類似的特征進(jìn)行打包(打包的過程一般稱作“抽象”),這樣就是封裝了。
舉個例子:大多數(shù)動物,都有眼睛耳朵嘴巴鼻子,把“五官”提取出來進(jìn)行封裝,這個封裝就成為了大多數(shù)動物共有的東西。
頭文件 coordinate.h
#ifndef __COORDINATE_H_
#define __COORDINATE_H_
//聲明一個位置類,屬性為坐標(biāo)x,y
typedef struct coordinate{
short int x;
short int y;
}COORDINATE_T,*P_COORDINATE_T;
extern P_COORDINATE_T coordinate_create(short int x,short int y);
extern void coordinate_destroy(P_COORDINATE_T p_coordinate);
extern void coordinate_moveby(P_COORDINATE_T p_coordinate,short int dx,short int dy);
extern short int coordinate_get_x(P_COORDINATE_T p_coordinate);
extern short int coordinate_get_y(P_COORDINATE_T p_coordinate);
extern void coordinate_test_function(void);
#endif // !__COORDINATE_H_
源文件 coordinate.c
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "inc/coordinate.h"
//創(chuàng)建一個coordinate對象
P_COORDINATE_T coordinate_create(short int x,short int y)
{
if((x < 0) || (y < 0)){
printf("coordinate creat error! x or y can not be less than zero n");
return NULL;
}
P_COORDINATE_T p_coordiante = NULL;
p_coordiante = (P_COORDINATE_T)malloc(sizeof(COORDINATE_T));
if(NULL != p_coordiante){
p_coordiante->x = x;
p_coordiante->y = y;
}
else printf("coordinate malloc error! n");
return p_coordiante;
}
//銷毀一個coordinate對象
void coordinate_destroy(P_COORDINATE_T p_coordiante)
{
if(NULL != p_coordiante){
free(p_coordiante);
p_coordiante = NULL;
}
}
//修改coordinate的屬性值
void coordinate_moveby(P_COORDINATE_T p_coordiante,short int dx,short int dy)
{
if(NULL != p_coordiante){
p_coordiante->x += dx;
p_coordiante->y += dy;
}
}
//獲取coordinate的屬性值x
short int coordinate_get_x(P_COORDINATE_T p_coordiante)
{
return (NULL != p_coordiante) ? p_coordiante->x : -1;
}
//獲取coordinate的屬性值y
short int coordinate_get_y(P_COORDINATE_T p_coordiante)
{
return (NULL != p_coordiante) ? p_coordiante->y : -1;
}
代碼比較簡單,在頭文件 coordinate.h里面,通過結(jié)構(gòu)體封裝了一個coordinate類,里面有兩個坐標(biāo)屬性 x 和 y 。
coordinate_create 函數(shù)主要用于創(chuàng)建一個 P_COORDINATE_T 類型的對象,并為其分配內(nèi)存空間,內(nèi)存分配成功后,設(shè)置兩個坐標(biāo)屬性的初始值,最后返回申請成功的對象指針。
coordinate_destroy 主要是釋放對象之前申請的內(nèi)存空間,然后把對象指針重置為NULL。
其他的操作函數(shù),主要是對類對象的屬性進(jìn)行操作,比如獲取 x 和 y 的屬性值,重置坐標(biāo)的屬性值。
以下是測試函數(shù),在主函數(shù)中調(diào)用,即可測試類coordinate對外提供的接口。
void coordinate_test_function(void)
{
P_COORDINATE_T p_coordiante_1 = NULL;
P_COORDINATE_T p_coordiante_2 = NULL;
p_coordiante_1 = (P_COORDINATE_T)coordinate_create(100,200);
p_coordiante_2 = (P_COORDINATE_T)coordinate_create(10,20);
if((NULL == p_coordiante_1) || (NULL == p_coordiante_2)){
printf("p_coordiante_1 or p_coordiante_2 create error! n");
return;
}
printf("p_coordiante_1 x = %d, y = %d n",coordinate_get_x(p_coordiante_1), coordinate_get_y(p_coordiante_1));
printf("p_coordiante_2 x = %d, y = %d n",coordinate_get_x(p_coordiante_2), coordinate_get_y(p_coordiante_2));
coordinate_moveby(p_coordiante_1,50,50);
coordinate_moveby(p_coordiante_2,50,50);
printf("after moveby p_coordiante_1 x = %d, y = %d n",coordinate_get_x(p_coordiante_1), coordinate_get_y(p_coordiante_1));
printf("after moveby p_coordiante_2 x = %d, y = %d n",coordinate_get_x(p_coordiante_2), coordinate_get_y(p_coordiante_2));
coordinate_destroy(p_coordiante_1);
coordinate_destroy(p_coordiante_2);
}
測試代碼比較簡單,主要是創(chuàng)建了兩個 P_COORDINATE_T 類型的對象,然后打印其坐標(biāo)初始值,再通過對外提供的函數(shù)修改其坐標(biāo)值,然后再打印出來,最后銷毀之前創(chuàng)建的對象。測試函數(shù)運(yùn)行后,結(jié)果如下所示:
p_coordiante_1 x = 100, y = 200
p_coordiante_2 x = 10, y = 20
after moveby p_coordiante_1 x = 150, y = 250
after moveby p_coordiante_2 x = 60, y = 70
從上述代碼可以看出,使用結(jié)構(gòu)體可以很好地對數(shù)據(jù)進(jìn)行封裝,并且需要通過指定的操作函數(shù)對結(jié)構(gòu)體內(nèi)的數(shù)據(jù)進(jìn)行訪問。
每個操作函數(shù)的第一個參數(shù)是對象本身的指針,通過這個指針去訪問具體對象里面的屬性。這是因?yàn)樵?C 語言中不存在像 C++ 語言那樣的 this 指針,所以我們只能顯式地通過函數(shù)傳參的方式,讓函數(shù)內(nèi)部可以訪問對象實(shí)例的其他成員。
對于對象屬性的各種操作函數(shù),還可以使用函數(shù)指針的方式,放入結(jié)構(gòu)體內(nèi)進(jìn)行封裝。但為了便于理解,本文并沒有采用這種方法。
源碼下載地址:https://github.com/embediot/my_program_test
感謝閱讀!