詳解C語言面向?qū)ο缶幊讨械姆庋b
前言
面向?qū)ο笫且环N思維方式,基本上用什么語言都是可以實現(xiàn)的。C語言的編程方式一般是面向過程的,但是也是可以實現(xiàn)面向?qū)ο蟮?。對象是什么?什么又是面向?qū)ο??面向?qū)ο蟮娜筇匦杂衷趺磳崿F(xiàn),且聽我細(xì)細(xì)道來。
一、面向?qū)ο蠡靖拍?/h2>
什么是對象?
此對象非彼對象,雖然有時候此對象又可以是你腦袋中的對象,那讓我們從我們誤解的對象開始了解吧,雖然我沒有,但是用一下自己的直男思維,想想一個對象也是可以滴。那我就進入一下我這個直男腦袋中的對象吧!我有一個對象,這個對象呢,膚白貌美大長腿。用詩中的話就是“皓腕凝霜雪,壚邊人似月”,美麗的江南女子,誰不喜歡呢。既然是想象,對象不只是僅僅是膚白貌美大長腿,還得愛你,在你寂寞的時候能陪你,在你難過時能在你身邊,在你打游戲的時候不會無理取鬧。這樣的對象多好哇,簡直就是夢中情人,可惜只存在想想中(嘆氣)。
通過上面的例子,想象中的對象,它具有了對象的特征,是不算非常符合人類的特性,易懂。那讓我們從上面的例子提取出來對象的普遍特征。
特征一:屬性
讓我們回到我想象中的對象,對象是怎樣的,皓腕凝霜雪,壚邊人似月,這個是對象的屬性,也就是樣子。當(dāng)然屬性不只是樣子啦,你可以添加更多的屬性,比如聲音好聽,年齡20歲等等。
特征二:行為
對象具有的動作就是行為。在上面的例子就是,對象非常愛你,難過的時候能陪你,寂寞的時候也能陪你等等,就是這個對象具有的動作,對象能干什么。
對象與類
我們知道了對象是什么,但是你有沒有發(fā)現(xiàn)這個對象是很廣泛的,也就是我想象中的對象不知有一個,符合我想象中對象的特征可以多個,也就是我可以想象又很多個對象。我可以想象有”后宮佳麗三千人“,這三個都符合我對象的特征。這些特征就是類,也就是符合我想象的人不只是只有一個,可以有多個,只要這個人符合我想象的特征,她就是在這個類下面的。
那么類與對象的關(guān)系又是怎樣的?對象就是符合這個類的特例,怎么理解呢?
在我的想象中,符合大美女的屬性有很多,但是我不知道具體是誰,但是有一天我在動漫中看見了這個人,她叫小A。小A就是在大美女類下面的一個對象。又有一天,我又碰到一個人,也符合我定義的大美女,這個對象,她叫小B。小A和小B都是符合定義的,也就是在這個類下面的,而小A和小B是一個特例,也就是這個類下面的人,是獨一無二的。
面向?qū)ο蟮木幊谭绞?/h3>
老生常談,先簡單看看面向過程的編程方式是怎樣的?設(shè)想一個場景,刺激點的。有一天杰哥想你了,打算和你回家一起打電動,那他該怎么做才能邀請到你回家一起打電動?
面向過程的解決方式:簡單點的方式簡化一下
1:他首先西裝革履,打扮的人模狗樣,看起來十分帥氣,小姐姐看了表示很贊
2:打車到你家
3:盛情邀約
面向?qū)ο蟮慕鉀Q方式又是怎樣的呢:我們看他邀請你涉及幾個對象,打車對象,邀請對象,打電動對象,回家對象。那對象是怎么做的呢?那讓我們看一下執(zhí)行順序:杰哥首先調(diào)用了打扮的對象進行了打扮,然后調(diào)用打車對象去了你家。到了你家后調(diào)用了邀請對象的行為發(fā)出了邀請,然后你調(diào)用了邀請對象的行為拒絕了杰哥,杰哥調(diào)用了情緒的對象的行為,發(fā)出了很難過的感覺。
打扮對象:
- 行為:打扮
打車對象:
- 屬性:打車人
- 屬性:打車地點
- 行為:上車
邀請對象:
- 屬性:邀請人
- 屬性:邀請結(jié)果
- 行為1:接受
- 行為2:拒絕
- 行為3:發(fā)出邀請
回家對象:
- 屬性:回家的路
- 屬性:回家的時間
- 行為:回家
情緒:
- 屬性:程度
- 行為1:傷心
- 行為2:難過
- 行為3:非常難過
通過上面的例子,大概了解到了與面向過程的區(qū)別了,面向?qū)ο蟮木幊谭绞降膯卧菍ο?,做了什么事情也是以對象?zhí)行動作。對象可以被很多對象調(diào)用,杰哥可以調(diào)用邀請對象中的邀請行為,你也可以調(diào)用邀請對象發(fā)出拒絕邀請的動作。對象的屬性是怎樣的,怎樣定義是靈活的。
看到上面的例子:面向?qū)ο蟮木幊谭绞?面向過程+面向?qū)ο?。對象將某一些行為高度封裝,然后由指揮官也就是我們自己按照自己的想法按照某個順序調(diào)用(面向過程),在過程中,對象之間會進行一定的數(shù)據(jù)交互與一定的對象之間的行為調(diào)用。
再舉個例子:實現(xiàn)一個循跡小車
構(gòu)建對象:傳感器 控制器 小車
小車對象:
- 屬性:當(dāng)前偏移值
- 行為:前進 ,后退 ,左轉(zhuǎn) ,右轉(zhuǎn)
控制器:
- 屬性:輸入值,輸出值
- 行為:計算控制值
傳感器:
- 屬性:傳感器測量值 傳感器數(shù)量 傳感器
- 行為:測量
//偽代碼
void Follow_mark(void)
{
調(diào)用傳感器對象進行測量,將測量值保存到器測量值
調(diào)用控制器對象,將傳感器測量值作為輸入?yún)?shù),計算得到結(jié)果進行保存
調(diào)用小車對象,根據(jù)控制傳入的控制值,計算當(dāng)前偏移量,然后根據(jù)偏移量調(diào)用左轉(zhuǎn)/右轉(zhuǎn)行為
}
二、C語言實現(xiàn)面向?qū)ο?/h2>
面向?qū)ο蟮娜筇卣?/h3>
了解了面向?qū)ο蟮乃枷耄?strong>思想是最重要的,特征是次要的。面向?qū)ο缶哂腥筇卣?,我們或多或少都可以實現(xiàn),java,python,C++都有,但是C也是可以實現(xiàn)的,只是會比較麻煩,三大特征分別是封裝,繼承,多態(tài)。這三大特征能夠幫助實現(xiàn)面向?qū)ο蟮木幊蹋沟妹嫦驅(qū)ο笞兊酶鼉?yōu)雅。我們先了解三大特征之三大特征之封裝。
面向?qū)ο笾庋b
簡介
封裝就是將對象的特征進行封裝,使之對象的屬性和行為只能通過對象進行訪問。在上面的例子中,邀請的對象,它的屬性與行為是被封裝好的,我們只能調(diào)用邀請這個對象才能調(diào)用邀請對象的行為。
優(yōu)勢:
1、隱藏內(nèi)部細(xì)節(jié),類似函數(shù),只需要調(diào)用這個邀請對象的行為發(fā)出邀請,而不需要知道里面的底層實現(xiàn)
2、更安全,復(fù)用性更好。對象的值都是被封裝好的,隱藏掉的,一般是程序員只會提供相應(yīng)的接口來訪問,不能直接修改。復(fù)用性,從上面的例子,誰都可以調(diào)用對象的行為。
代碼實現(xiàn)–基礎(chǔ)版
基礎(chǔ)版不涉及函數(shù)指針與函數(shù)表,先學(xué)習(xí)這個基礎(chǔ)版的,理解好面向?qū)ο蟮淖詈唵蔚姆庋b的實現(xiàn)。
在實現(xiàn)前我們先想一想C到底有什么結(jié)構(gòu)可以實現(xiàn)封裝屬性,各種屬性。這個很簡單,結(jié)構(gòu)體嘛,能放各種類型的屬性。
行為又怎么體現(xiàn)呢,可以實現(xiàn)各種行為,函數(shù)嘛。后面的多態(tài)會涉及函數(shù)指針,使用函數(shù)指針可以實現(xiàn)多態(tài),這都是后面的事情,后面的文章會有簡介。
那讓我們做一個PID控制器的對象吧,如果不懂的小伙伴也沒關(guān)系,這個只是控制器,有輸入,輸出,調(diào)試參數(shù),了解這些就行了。具體實現(xiàn)過程,內(nèi)部細(xì)節(jié)不懂也沒關(guān)系,這個不重要,我代碼會標(biāo)出來的。
那我們直接閱讀代碼,進入困難模式:代碼會有比較詳細(xì)的注釋,很容易看懂!
//開始構(gòu)造對象,既然是控制器,對象必須具有輸入,輸出,調(diào)試參數(shù)
//屬性就是:參數(shù)值,輸入值,輸出值
//行為就是:設(shè)置參數(shù),查看參數(shù),根據(jù)輸入計算輸出,構(gòu)造對象,刪除對象
//屬性:用結(jié)構(gòu)體實現(xiàn)
#include "stdio.h"
#include <string.h>
#include <stdlib.h>
//控制器對象
//控制器對象屬性
typedef struct
{
int input;/*控制器輸入*/
int ouput;/*控制器輸出*/
int P_parameter,I_parameter,D_parameter;/*控制調(diào)試參數(shù)*/
int Sum_error;/*總偏差,位置式PID積分相關(guān)的參數(shù)*/
int Last_error;/*上次偏差,位置式PID積分相關(guān)的參數(shù)*/
}controller;
//構(gòu)造對象,初始化
controller *Ctor_controller(void)
{
controller *temp;
temp=(controller *)malloc(sizeof(controller));
//清零
memset(temp,0,sizeof(controller));
return temp;
}
//刪除對象
void Del_ontroller(controller * const Me)
{
if(Me!=NULL) free(Me);
}
//設(shè)置控制器參數(shù)
void Write_controller(controller * const Me,int P,int I,int D)
{
Me->P_parameter=P;
Me->I_parameter=I;
Me->D_parameter=D;
}
//讀取控制器參數(shù)的值
controller Read_controller(const controller * const Me,int P,int I,int D)
{
return (*Me);
}
//計算控制器輸出,細(xì)節(jié)看不懂沒關(guān)系,只需要知道傳入的是偏差,就會有輸出一個計算結(jié)果就行,這個結(jié)果能夠幫助控制
//至于偏差怎么定義什么時候需要用到PID控制器就知道了
int Out_controller(controller * const Me,int input)
{
float iIncpid=0;
int now_error=input;//當(dāng)前偏差
Me->Sum_error+=input;
//積分量限幅,方式積分飽和過深
if(Me->Sum_error >500)
{
Me->Sum_error = 500 ;
}
if(Me->Sum_error < -500)
{
Me->Sum_error = -500 ;
}
Me->ouput=Me->P_parameter * input // P
+Me->I_parameter * Me->Sum_error // I
+Me->D_parameter * (now_error-Me->Last_error); // D
Me->Last_error=now_error; // 存儲誤差,用于下次計算
return(Me->ouput); // 返回計算值
}
int main()
{
controller *test;
controller read_val;
//構(gòu)造,創(chuàng)建一個對象
test=Ctor_controller();
//設(shè)置對象的值
Write_controller(test,1,1,1);
//查看對象的值
read_val=Read_controller(test,1,1,1);
printf("對象 P= %d I=%d D=%d \r\n",read_val.P_parameter,read_val.I_parameter,read_val.D_parameter);
//調(diào)用控制器一次:
printf("控制器輸出=%d \r\n",Out_controller(test,100));
//刪除/銷毀一個對象
Del_ontroller(test);
}
輸出結(jié)果:
對象 P= 1 I=1 D=1
控制器輸出=300
從上面的例子可以看出來,我直接調(diào)用對象,就可以實現(xiàn)封裝,設(shè)置,查看等,注意使用了需要手動調(diào)用刪除,不然容易出現(xiàn)內(nèi)存泄漏,對象的生存時間就是我們程序員自己釋放前的時間。
這里是使用堆的方式,容易出現(xiàn)內(nèi)存溢出的情況,如果是單片機等其他資源較小的單元,可以使用其他方式構(gòu)造對象,比如下面:對象的生存時間就是主函數(shù)的結(jié)束時間,編譯器替我們釋放了對象的資源,不需要我們主動進行釋放。
#include "stdio.h"
#include <string.h>
#include <stdlib.h>
//控制器對象
//控制器對象屬性
typedef struct
{
int input;/*控制器輸入*/
int ouput;/*控制器輸出*/
int P_parameter,I_parameter,D_parameter;/*控制調(diào)試參數(shù)*/
int Sum_error;/*總偏差,位置式PID積分相關(guān)的參數(shù)*/
int Last_error;/*上次偏差,位置式PID積分相關(guān)的參數(shù)*/
}controller;
//構(gòu)造對象,初始化
void Ctor_controller(controller * const Me)
{
//清零
memset(Me,0,sizeof(controller));
}
//刪除對象
void Del_ontroller(controller * const Me)
{
;
}
//設(shè)置控制器參數(shù)
void Write_controller(controller * const Me,int P,int I,int D)
{
Me->P_parameter=P;
Me->I_parameter=I;
Me->D_parameter=D;
}
//讀取控制器參數(shù)的值
controller Read_controller(const controller * const Me,int P,int I,int D)
{
return (*Me);
}
//計算控制器輸出,細(xì)節(jié)看不懂沒關(guān)系,只需要知道傳入的是偏差,就會有輸出一個計算結(jié)果就行,這個結(jié)果能夠幫助控制
//至于偏差怎么定義什么時候需要用到PID控制器就知道了
int Out_controller(controller * const Me,int input)
{
float iIncpid=0;
int now_error=input;//當(dāng)前偏差
Me->Sum_error+=input;
//積分量限幅,方式積分飽和過深
if(Me->Sum_error >500)
{
Me->Sum_error = 500 ;
}
if(Me->Sum_error < -500)
{
Me->Sum_error = -500 ;
}
Me->ouput=Me->P_parameter * input // P
+Me->I_parameter * Me->Sum_error // I
+Me->D_parameter * (now_error-Me->Last_error); // D
Me->Last_error=now_error; // 存儲誤差,用于下次計算
return(Me->ouput); // 返回計算值
}
int main()
{
controller test;
controller read_val;
//構(gòu)造,創(chuàng)建一個對象
Ctor_controller(&test);
//設(shè)置對象的值
Write_controller(&test,1,1,1);
//查看對象的值
read_val=Read_controller(&test,1,1,1);
printf("對象 P= %d I=%d D=%d \r\n",read_val.P_parameter,read_val.I_parameter,read_val.D_parameter);
//調(diào)用控制器一次:
printf("控制器輸出=%d \r\n",Out_controller(&test,100));
}
代碼實現(xiàn)-進階版
到進階版,才能夠完整的看到封裝的實現(xiàn),封裝里面就具有了對象的屬性與行為。這里我們通過函數(shù)指針訪問對象的行為,我們可以通過函數(shù)指針訪問對象的行為。
那具體行為是怎么實現(xiàn)的呢?實現(xiàn)是通過函數(shù)表中的函數(shù)指針來訪問函數(shù),以此來實現(xiàn)不同函數(shù)的調(diào)用,從而實現(xiàn)對象的行為。
那讓我們看一下代碼實現(xiàn),然后分析指針指向就知道函數(shù)是怎么實現(xiàn)的。
頭文件 :定義了對象的屬性與行為
#ifndef __OOP_H
#define __OOP_H
//控制器對象
struct controller_vtbl;
typedef struct
{
//對象屬性
int input;/*控制器輸入*/
int ouput;/*控制器輸出*/
int P_parameter,I_parameter,D_parameter;/*控制調(diào)試參數(shù)*/
int Sum_error;/*總偏差,位置式PID積分相關(guān)的參數(shù)*/
int Last_error;/*上次偏差,位置式PID積分相關(guān)的參數(shù)*/
//對象行為指針,通過指針訪問函數(shù)
struct controller_vtbl *vptr;
}controller;
//對象的行為所在表,定義對象的行為在這里,通過定義函數(shù)指針指向需要實現(xiàn)對象行為的指針
struct controller_vtbl
{
controller * (*Ctor_controller)(void);
void (*Del_controller)(controller * const Me);
controller (*Read_controller)(const controller * const Me);
void (*Write_controller)(controller * const Me,int P,int I,int D);
int (*Out_controller)(controller * const Me,int input);
};
//對象行為函數(shù)
controller * Ctor_controller(void);
void Del_controller(controller * const Me);
controller Read_controller(const controller * const Me);
void Write_controller(controller * const Me,int P,int I,int D);
int Out_controller(controller * const Me,int input);
#endif
源文件::具體函數(shù)的行為屬性的實現(xiàn)就在這里
//開始構(gòu)造對象,既然是控制器,對象必須具有輸入,輸出,調(diào)試參數(shù)
//屬性就是:參數(shù)值,輸入值,輸出值
//行為就是:設(shè)置參數(shù),查看參數(shù),根據(jù)輸入計算輸出,構(gòu)造對象,刪除對象
//屬性:用結(jié)構(gòu)體實現(xiàn)
#include "stdio.h"
#include <string.h>
#include <stdlib.h>
#include "temp.h"
//構(gòu)造對象,初始化
controller * Ctor_controller(void)
{
controller *ptr;
struct controller_vtbl *table;
ptr=(controller *)malloc(sizeof(controller));
table=(struct controller_vtbl *)malloc(sizeof(struct controller_vtbl));
//清零
memset(ptr,0,sizeof(controller));
table->Ctor_controller=&Ctor_controller;
table->Del_controller=&Del_controller;
table->Out_controller=&Out_controller;
table->Write_controller=&Write_controller;
table->Read_controller=&Read_controller;
ptr->vptr=table;
return ptr;
}
//刪除對象/析構(gòu)對象
void Del_controller(controller * const Me)
{
if(Me!=NULL)
{
free(Me->vptr);
free(Me);
}
}
//設(shè)置控制器參數(shù)
void Write_controller(controller * const Me,int P,int I,int D)
{
Me->P_parameter=P;
Me->I_parameter=I;
Me->D_parameter=D;
}
//讀取控制器參數(shù)的值
controller Read_controller(const controller * const Me)
{
return (*Me);
}
//計算控制器輸出,細(xì)節(jié)看不懂沒關(guān)系,只需要知道傳入的是偏差,就會有輸出一個計算結(jié)果就行,這個結(jié)果能夠幫助控制
//至于偏差怎么定義什么時候需要用到PID控制器就知道了
int Out_controller(controller * const Me,int input)
{
float iIncpid=0;
int now_error=input;//當(dāng)前偏差
Me->Sum_error+=input;
//積分量限幅,方式積分飽和過深
if(Me->Sum_error >500)
{
Me->Sum_error = 500 ;
}
if(Me->Sum_error < -500)
{
Me->Sum_error = -500 ;
}
Me->ouput=Me->P_parameter * input // P
+Me->I_parameter * Me->Sum_error // I
+Me->D_parameter * (now_error-Me->Last_error); // D
Me->Last_error=now_error; // 存儲誤差,用于下次計算
return(Me->ouput); // 返回計算值
}
int main()
{
controller *test;
controller read_val;
//構(gòu)造,創(chuàng)建一個對象,返回對象指針
test=Ctor_controller();
//設(shè)置對象的值
test->vptr->Write_controller(test,1,1,1);
//查看對象的值
read_val=test->vptr->Read_controller(test);
printf("對象 P= %d I=%d D=%d \r\n",read_val.P_parameter,read_val.I_parameter,read_val.D_parameter);
//調(diào)用控制器一次:
printf("控制器輸出=%d \r\n",test->vptr->Out_controller(test,100));
//刪除/銷毀一個對象
test->vptr->Del_controller(test);
}
運行結(jié)果:
對象 P= 1 I=1 D=1
控制器輸出=300
可以看到,我們這次操作對象并不是直接調(diào)用函數(shù),而是通過指針的方式來訪問具體的哪個函數(shù),而指針是在創(chuàng)建的對象里面的,這樣就可以直接通過對象來訪問它的行為。
后面實現(xiàn)多態(tài)也是使用了函數(shù)指針的方式,在多態(tài)里面這里的指針與行為表有了它自己的名字,就是虛指針與虛表。
總結(jié)
知道了什么是對象,對象與類的關(guān)系,以及如何實現(xiàn)面向?qū)ο蟮?strong>封裝,下一篇將了解到面向?qū)ο蟮?strong>繼承,如何用C語言來實現(xiàn),例子依然是PID控制器,不過會對控制器進行繼承,更高級的實現(xiàn)。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C++超詳細(xì)講解隱藏私有屬性和方法的兩種實現(xiàn)方式
為了避免因為將類庫中的私有成員開放給類的使用方而導(dǎo)致的軟件邏輯外泄,因此需要將對外代碼中的私有成員隱藏起來,下面我們來了解一下隱藏私有屬性和方法的兩種實現(xiàn)方式2022-05-05
C++使用HDF5庫實現(xiàn)將h5圖像轉(zhuǎn)為tif格式
這篇文章主要為大家詳細(xì)介紹了C++ 語言如何通過hdf5庫與gdal庫實現(xiàn)將.h5格式的多波段HDF5圖像批量轉(zhuǎn)換為.tif格式,有需要的可以參考一下2024-12-12
C語言使用openSSL庫DES模塊實現(xiàn)加密功能詳解
這篇文章主要介紹了C語言使用openSSL庫DES模塊實現(xiàn)加密功能,簡單講解了DES加密的相關(guān)概念,并結(jié)合實例形式分析了DES加密的具體實現(xiàn)技巧,需要的朋友可以參考下2017-05-05

