本文中的例子有這樣兩個(gè)概念:任務(wù)(Task),執(zhí)行器(Executor)。任務(wù)有名稱(taskName),并且可以執(zhí)行(execute)。 而執(zhí)行器與具體任務(wù)所執(zhí)行的內(nèi)容無關(guān),只是回調(diào)(callback)任務(wù)的執(zhí)行方法,這樣我們的執(zhí)行器就可以做的比較通用。而任務(wù)接口只需要實(shí)現(xiàn)一個(gè)execute方法即可,這樣我們的任務(wù)就可以是多種多樣的,可以通過統(tǒng)一的接口set給執(zhí)行器執(zhí)行。這是面向?qū)ο笾谢镜乃枷?,也是比較常用的抽象方式。下面我們具體看下例子。

創(chuàng)新互聯(lián)專注于企業(yè)全網(wǎng)營(yíng)銷推廣、網(wǎng)站重做改版、聞喜網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5頁面制作、商城系統(tǒng)網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為聞喜等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
可以想象,main函數(shù)大概是這個(gè)樣子:
int main(int argc, char** argv) {
Task *t1 = TaskConstruction("Task1", run);//此處的run是一個(gè)函數(shù)指針 Executor *exe = ExecutorConstruction(); exe->setTask(t1); exe->begin(); exe->cancel();
Task *t2 = TaskConstruction("Task2", run2);//此處的run2也是一個(gè)函數(shù)指針,用于構(gòu)造一個(gè)Task. exe->setTask(t2); exe->begin(); exe->cancel(); return (EXIT_SUCCESS); } |
運(yùn)行結(jié)果為:
task : [Task1] is ready to run [a = 1.200000, b = 2.300000] [(a + b) * (a - b) = -3.850000] cancel is invoked here task : [Task2] is ready to run another type of execute,just print out some information cancel is invoked here |
好了,下面詳細(xì)看看實(shí)現(xiàn):
定義接口
首先,定義Task和Executor兩個(gè)實(shí)體的接口:
Task接口,注意其中的_this字段,這個(gè)指針在后邊有很重要的作用,用于hold整個(gè)Task的實(shí)例。然后是一個(gè)taskName的字符串,和一個(gè)函數(shù)指針,這個(gè)指針在初始化(構(gòu)造)Task時(shí)傳入。這個(gè)execute()函數(shù)比較有意思,它不在內(nèi)部使用,而是讓執(zhí)行器回調(diào)執(zhí)行的。
#ifndef _ITASK_H #define _ITASK_H
typedef struct Task{ struct Task *_this; char *taskName; void (*execute)(); }Task;
void execute(); #endif /* _ITASK_H */ |
執(zhí)行器接口比Task接口復(fù)雜一些,其中包含_this指針,包含一個(gè)對(duì)Task的引用,然后是對(duì)外的接口begin(), cancel().對(duì)接口的使用者來說,他們只需要調(diào)用接口實(shí)例上的setTask(),將任務(wù)傳遞給執(zhí)行器,然后在適當(dāng)時(shí)期調(diào)用begin(),等待任務(wù)正常結(jié)束或者調(diào)用cancel()將其取消掉。
#include "ITask.h"
#ifndef _IEXECUTOR_H #define _IEXECUTOR_H
typedef struct Executor{ struct Executor *_this; Task *task; char *(*setTask)(Task* task); void (*begin)(); void (*cancel)(); }Executor;
char *setTask(Task *task); void begin(); void cancel();
#endif /* _IEXECUTOR_H */
|
實(shí)現(xiàn)接口
#include
#include "ITask.h"
Task *task = NULL;
void execute();
/*
* The construction of Task object.
* name : the name of the task
* execute : execute method of the task
*
*/
Task *TaskConstruction(char *name, void (*execute)()){
task = (Task*)malloc(sizeof(strlen(name))+sizeof(execute));
task->taskName = name;
task->execute = execute;
task->_this = task;
return (Task*)task;//返回一個(gè)自身的指針,通過內(nèi)部的_this指針,兩者即可實(shí)現(xiàn)封裝
}
/*
* Destruction of task, not used current time.
*
*/
void TaskDestruction(){
task->taskName = NULL;
task->execute = NULL;
task->_this = NULL;
task = NULL;
}
/*
* private method, should register to executor
*
*/
void execute(){
task->_this->execute();//調(diào)用_this上的execute()方法
}
|
執(zhí)行器的實(shí)現(xiàn)一樣,稍微復(fù)雜一點(diǎn),構(gòu)造的時(shí)候,將函數(shù)指針在內(nèi)部設(shè)置好,當(dāng)外部調(diào)用時(shí)動(dòng)態(tài)的執(zhí)行需要執(zhí)行的函數(shù),這句話可能有些繞口,這么看:在構(gòu)造Executor的時(shí)候,executor->begin = begin; 這條語句是將下面void begin()的實(shí)現(xiàn)注冊(cè)到結(jié)構(gòu)體中,但是要執(zhí)行什么還是不確切的,當(dāng)setTask以后,回調(diào)函數(shù)的地址已經(jīng)明確:
(executor->_this->task = task;),此時(shí)調(diào)用begin()即可正確的調(diào)用到注冊(cè)的Task上。
#include
#include "IExecutor.h"
Executor *executor = NULL;
Executor *ExecutorConstruction(){
executor = (Executor*)malloc(sizeof(Executor));
executor->begin = begin;
executor->cancel = cancel;
executor->setTask = setTask;
executor->_this = executor;
return (Executor*)executor;
}
void ExecutorDestruction(){
executor->begin = NULL;
executor->cancel = NULL;
executor->setTask = NULL;
executor = NULL;
}
char *setTask(Task *task){
executor->_this->task = task;
}
void begin(){
printf("task : [%s] is ready to run\n",executor->_this->task->taskName);
executor->_this->task->execute();
}
void cancel(){//這個(gè)函數(shù)沒有實(shí)現(xiàn),只是做了一個(gè)占位符,以后如果有多線程,可以用來停止主動(dòng)線程。
printf("cancel is invoked here\n");
}
|
其實(shí),兩個(gè)實(shí)現(xiàn)的代碼都不算復(fù)雜,如果對(duì)C的指針理解的稍好,基本就沒什么問題了。
在C中使用OO
為了試驗(yàn),我們不妨設(shè)計(jì)兩個(gè)不同的Task,一個(gè)Task是計(jì)算兩個(gè)數(shù)的某四則混合運(yùn)算,另一個(gè)僅僅是用來打印一點(diǎn)信息。然后我們可以看到,他們使用完全相同的接口來執(zhí)行:
#include
void run(){//計(jì)算(a+b)*(a-b)
float a, b, r;
a = 1.2;
b = 2.3;
r = 0.0;
printf("[a = %f, b = %f]\n", a, b);
printf("[(a + b) * (a - b) = %f]\n",((a+b)*(a-b)));
}
void run2(){//打印一句話,事實(shí)上,這些函數(shù)可以做任何事,比如I/O,網(wǎng)絡(luò),圖片處理,音樂播放等等。
printf("another type of execute,");
printf("just print out some information\n");
}
|
然后,在Main中獎(jiǎng)他們注冊(cè)給Task,代碼如下所示:
#include
#include
#include "ITask.h"
#include "IExecutor.h"
extern void run();
extern void run2();
int main(int argc, char** argv) {
//代碼的風(fēng)格上,應(yīng)該可以看出和OO的風(fēng)格及其類似。
Task *t1 = TaskConstruction("Task1", run);//new Task("Task 1", run);
Executor *exe = ExecutorConstruction();// new Executor();
exe->setTask(t1);
exe->begin();
exe->cancel();
Task *t2 = TaskConstruction("Task2", run2);
exe->setTask(t2);
exe->begin();
exe->cancel();
return (EXIT_SUCCESS);
}
|
程序的輸出結(jié)果上文中已經(jīng)可以看到了,這里就不貼了。
當(dāng)然,本文的主要目的不是想說什么“C也可以實(shí)現(xiàn)面向?qū)ο蟆敝惖挠字捎^點(diǎn),只要誰沒有嚴(yán)重的自虐傾向,相信不會(huì)有誰真的會(huì)用C來做OO的開發(fā)。只是想表達(dá)一下,指針在C中的重要性和指針的一點(diǎn)高級(jí)用法。其實(shí)現(xiàn)在的OO語言,基本還是以面向過程的表達(dá)式來表達(dá)面向?qū)ο蠖?。并沒有什么神奇之處,OO主要是思想上的抽象,可以說是語言無關(guān)的(language independent)。
當(dāng)前名稱:講述C和指針的故事
網(wǎng)站鏈接:
http://m.fisionsoft.com.cn/article/ccddojd.html