【C/C++學習筆記】c++ 回調函數

1.介紹

回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作爲參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。(百度百科)

引用知乎一個對回調函數的說明:回調函數的整個過程就好比是,你到一個商店買東西,剛好你要的東西沒有貨,於是你在店員那裏留下了你的電話,過了幾天店裏有貨了,店員就打了你的電話,然後你接到電話後就到店裏去取了貨。在這個例子裏,你的電話號碼就叫回調函數,你把電話留給店員就叫登記回調函數,店裏後來有貨了叫做觸發了回調關聯的事件,店員給你打電話叫做調用回調函數,你到店裏去取貨叫做響應回調事件。

2.機制

⑴定義一個回調函數;
⑵提供函數實現的一方在初始化的時候,將回調函數的函數指針註冊給調用者;
⑶當特定的事件或條件發生的時候,調用者使用函數指針調用回調函數對事件進行處理。

3.意義和作用

因爲可以把調用者與被調用者分開,所以調用者不關心誰是被調用者。它只需知道存在一個具有特定原型和限制條件的被調用函數。簡而言之,回調函數就是允許用戶把需要調用的函數的指針作爲參數傳遞給一個函數,以便該函數在處理相似事件的時候可以靈活的使用不同的方法。

回調可用於通知機制。在上層模塊和底層模塊交互時,上層可以調用底層的函數,但是底層不可以調用上層函數。這時就可以使用回調函數,通過上層調用底層的註冊回調接口,傳入回調指針,實現底層通知上層的作用。

另外 回調函數的一個好處是,可以通過回調函數,調用不同接口。比如接收數據,不同數據需要不同的解析函數。但是接收數據的流程是相同的,這樣使用回調函數,在接收數據的時候就可以不用判斷,而是自動使用不同的解析函數。總之 使用回調函數,可以降低耦合,實現多樣性。

4.實現

1.首先介紹最簡單的回調函數調用方法,直接通過指針函數,調用回調函數,這邊是直接把回調函數的設置和觸發放到一起。

#include <stdio.h>

//返回值(*指針名)(參數列表)
typedef int(*callback)(int, int);

//回調函數
int SUB(callback pfunc, int a, int b){
	return pfunc(a, b);//此處回調add函數...
}
//普通函數
int sub(int a, int b){
	return a - b;
}

int main()
{
	printf("%d\n", sub(5, 2));
	printf("%d\n", SUB(sub, 5, 2));
	return 0;
}

2. 通過調用動態庫的回調函數

這邊給個簡單的測試例子,先封裝一個含有回調函數的dll。cpp文件內容如下:

//聲明
typedef int(*pfunc)(int, int);
static pfunc myadd; //連接狀態的回調函數

//設置回調函數
void CALLBACK set_add(pfunc add)
{
	myadd = add;
}

int CALLBACK add(int x, int y)
{
	return myadd(x, y);
}


int CALLBACK sub(pfunc sub, int x, int y)
{
	return sub(x, y);
}

添加def文件, LIBRARY 後替換成自己的工程名稱,設置導出的接口。

LIBRARY "CallBackDemo"

EXPORTS
add       @1
set_add   @2
sub       @3

新建一個工程,調用dll。

int addTest(int a, int b){
	return a + b;
}

int subTest(int a, int b){
	return a - b;
}

// 回調函數必須是靜態成員函數或者全局函數來實現回調函數
typedef int(*pfunc)(int, int);

typedef void (CALLBACK *pfunc1)(padd);
typedef int (CALLBACK *pfunc2)(int, int);
typedef int (CALLBACK *pfunc3)(padd, int, int);
int main()
{
	HINSTANCE hDll = LoadLibrary(L"CallbackDemo.dll");
	if (NULL == hDll)
	{
		std::cout << "load library add.dll failed" << std::endl;
        return 0;
	}
	pfunc1 pfSetAdd= (pfunc1)GetProcAddress(hDll, "set_add");
	pfunc2 pfAdd = (pfunc2)GetProcAddress(hDll, "add");
	pfunc3 pfSub = (pfunc3)GetProcAddress(hDll, "sub");
	pfSetAdd(addTest);
	int sum = pfAdd(3, 5);
	printf("%d\n", sum);
	printf("%d\n", pfSub(subTest, 5, 2));
	FreeLibrary(hDll);
	hDll = NULL;
	system("pause");
	return 0;

}

注意:

(1)在windows程序中,回調函數必須遵循_stdcall調用約定,所以我們聲明回調函數時要使用CALLBACK(_stdcall的宏定義)。使用CALLBACK而不是_stdcall的原因是爲了告訴我們這是一個回調函數。

(2)底層dll聲明的回調函數,必須是靜態成員或者全局函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章