目錄
1.引言
鉤子函數、回調函數、註冊函數,掛鉤子這些我們代碼中經常涉及到的東西,是否已經困擾你很久了?它們究竟是怎麼回事,究竟怎麼用?下面我來爲你一一解答。
什麼是鉤子函數?
鉤子函數也叫回調函數,是通過函數指針來實現的,那我們來看看什麼是函數指針。
2.變量指針
首先看看以下例子:
我們可以讓指針p先後指向a, b,這樣,p就先後代表了不同變量的地址
p = &a;
p = &b;
3.函數指針
同樣地,函數的指針可以指向不同的函數,從而完成不同的功能。
例如,定義函數指針:
int (* g_pFun) (int x, int y);
有兩個函數:
/*返回兩個參數中的最大值*/
int Max(int x, int y)
{
}
/*返回兩個參數中的最小值*/
int Min(int x, int y)
{
}
int main(int argc, char* argv[])
{
int r;
/*我們讓函數指針先後指向不同的函數*/
int a = 10;
int b = 15;
g_pFun = Max;
r= g_pFun(a, b); /*相當於執行函數Max*/
printf("%d\n", r);
g_pFun = Min;
r= g_pFun(a, b); /*相當於執行函數Min*/
printf("%d\n", r);
return 0;
}
分別輸出:15
10
這樣,同樣調用g_fun ,兩次卻完成不同的功能,神奇吧?這就是函數指針的妙用。
Max,Min函數就是鉤子函數了,把函數指針g_pFun指向函數Max,Min的過程,就是“掛鉤子”的過程,把鉤子函數“掛”到函數指針上,很形象。
4.鉤子函數作用
有人可能有疑問,那麼這裏爲什麼不直接調用Max和Min函數呢?
這是因爲,我們在寫main函數的時候,可能還不知道它會完成什麼功能,這時候留下函數指針作爲接口,可以掛上不同的函數完成不同的功能,究竟執行什麼功能由鉤子函數的編寫者完成。
5.鉤子函數使用
那我們平時怎麼用的呢?
在我們的代碼中,常常把掛鉤子的過程叫做註冊,會提供一個註冊函數,讓使用者把自己編寫的鉤子函數掛在已經聲明的函數指針上,這個註冊函數的參數就是我們的函數指針了,比如,我們可以給剛纔的函數指針提供一個註冊函數:
int RegFun( int (* pFun)(int x, int y) ) /*註冊函數的參數是函數指針*/
{
g_pFun = pFun;
return 0;
}
調用RegFun(Max)和RegFun(Min),就可以把鉤子函數掛上去了。
注意:爲了便於使用,函數指針往往被聲明爲全局變量,這也是剛纔把函數指針的名字命名爲g_pFun的原因。
下面我們來進行一下實戰演習,比如,平臺部分要執行某一個操作,但是具體的操作還不確定,我們完成這樣的代碼:
int (* g_pFun) (int x, int y); /*函數指針*/
int Plat()
{
int r;
int a = 10;
int b = 15;
r= g_pFun(a, b); /*這裏要做一個操作,但是具體的操作還不確定*/
printf("%d\n", r);
return 0;
}
另外,平臺部分再提供一個註冊函數:
int RegFun(int (* pFun)(int x, int y))
{
g_pFun = pFun;
return 0;
}
應用模塊完成具體的函數的功能:
int Max(int x, int y)
{
if(x>y)
return x;
else
return y;
}
int Min(int x, int y)
{
if(x<y)
return x;
else
return y;
}
因爲應用模塊無法修改平臺的代碼,只能調用平臺提供的註冊函數:
如果應用模塊註冊:
RegFun(Max);
則運行 main 函數時,輸出:15
如果應用模塊註冊:
RegFun(Min)
運行 main 函數時,輸出:10
這樣,平臺部分無需修改任何代碼,只是應用模塊註冊了不同的鉤子函數,就能夠完成不同的功能,這就是鉤子函數的妙用。
————————————————
版權聲明:本文爲CSDN博主「DyLan985」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/sunstars2009918/article/details/39340449
6.帶參數的鉤子函數
眼尖的朋友可能發現了,前面的例子裏面回調函數是沒有參數的,那麼我們能不能回調那些帶參數的函數呢?答案是肯定的。那麼怎麼調用呢?
#include<stdio.h>
int Callback_1(int x) // Callback Function 1
{
printf("Hello, this is Callback_1: x = %d ", x);
return 0;
}
int Callback_2(int x) // Callback Function 2
{
printf("Hello, this is Callback_2: x = %d ", x);
return 0;
}
int Callback_3(int x) // Callback Function 3
{
printf("Hello, this is Callback_3: x = %d ", x);
return 0;
}
int Handle(int y, int (*Callback)(int))
{
printf("Entering Handle Function. ");
Callback(y);
printf("Leaving Handle Function. ");
}
int main()
{
int a = 2;
int b = 4;
int c = 6;
printf("Entering Main Function. ");
Handle(a, Callback_1);
Handle(b, Callback_2);
Handle(c, Callback_3);
printf("Leaving Main Function. ");
return 0;
}
運行結果:
Entering Main Function.
Entering Handle Function.
Hello, this is Callback_1: x = 2
Leaving Handle Function.
Entering Handle Function.
Hello, this is Callback_2: x = 4
Leaving Handle Function.
Entering Handle Function.
Hello, this is Callback_3: x = 6
Leaving Handle Function.
Leaving Main Function.
可以看到,並不是直接把int Handle(int (*Callback)()) 改成 int Handle(int (*Callback)(int)) 就可以的,而是通過另外增加一個參數來保存回調函數的參數值,像這裏 int Handle(int y, int (*Callback)(int)) 的參數 y。同理,可以使用多個參數的回調函數。