1. 程式人生 > >C++ 中的回撥函式

C++ 中的回撥函式

關於回撥函式

回撥函式在百度百科的講解是這樣的

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

      通俗的理解 就是 有兩個函式A,B,在函式B中有指向A函式的指標,函式B可以通過A函式的指標來呼叫A函式。

下面分三種情況來講回撥函式

1  回撥函式是全域性函式

   這種情況比較簡單了  直接看程式碼

typedef void(*pFun)(int);
void fun1(int a)
{
	cout << a << endl;

}

void  fun2(pFun pf,int m)
{
	pf(m);
}


int main()
{
	pFun p;
	p = fun1;
	fun2(p, 10);
	return 0;
}
列印的結果是 10

程式第一句 定義了一個函式指標,該函式接收1個int行形參,返回值為空,在main函式中,第一句定義一個函式指標型別的p,p可以指向一個接收一個int型別的引數,返回值為空的函式,第二句把 fun1函式賦值給p,第三句把p做為引數傳遞給fun2,在fun2中就可以呼叫fun1了。

      在C++中 大多數情況我們用到的都是類,那麼在類中 類的非靜態成員函式可以可以作為回撥函式呢  答案是不可以的 ,因為普通的C++成員函式都隱含了一個傳遞函式作為引數,亦即“this”指標,C++通過傳遞this指標給其成員函式從而實現程式函式可以訪問C++的資料成員。這也可以理解為什麼C++類的多個例項可以共享成員函式卻-有不同的資料成員。由於this指標的作用,使得將一個成員函式作為回撥函式安裝時就會因為隱含的this指標使得函式引數個數不匹配,從而導致回撥函式呼叫失敗,要解決這個問題有兩種方法

  第一種  既然回撥函式不能有this指標 那麼我們可以 把回撥函式宣告為靜態的  程式碼如下

#include "stdafx.h"
#include <iostream>
using namespace std;

typedef void(*pFun)(int);
class B
{
public:
	B()
	{
		pf = NULL;
	}
	void  Show(int s)
	{
		pf(s);
	}
	void SetPf(pFun back)
	{
		if (back == NULL)
			return;
		this->pf = back;
	}

private:
	pFun pf;//宣告一個函式指標

};


class A
{
public:
	static void BackShow(int m)
	{
		cout << "回撥函式被呼叫:" <<m<< endl;
	}

};


int main()
{
	B *b = new B();
	b->SetPf(A::BackShow);
	b->Show(10);
	return 0;
}
輸出結果為: 回撥函式被呼叫:10  

我們把A的靜態成員函式地址 傳遞給b的pf,然後呼叫b的pf自然就呼叫的A的BackSHow方法了 ,但是在實際應用中,回撥函式在大部分情況是需要訪問類成員變數或者成員函式做一些事情的,如果把回撥函式設為static,這樣就需要類的成員變數和成員方法都設定成static  ,不和實際情況,解決方法是在類的靜態成員做為回撥函式的時候,給該回調函式傳遞一個this指標 程式碼如下

<pre name="code" class="cpp">#include "stdafx.h"
#include <iostream>
using namespace std;

typedef void(*pFun)(void*,int);

class A
{
public:
	static void backcall(void* a, int m)
	{
		A *af = (A*)a;
		af->show(m);
	}
	void show(int m)
	{
		cout << "A中show函式被呼叫:" << m << endl;
	}

};


class B
{
public:
	B()
	{
		a = NULL;
		pf = NULL;
	}
	void  CalledA(int m)
	{
		pf(a, m);
	}
	void SetA(A *a)
	{
		this->a = a;
		
	}
	void SetPf(pFun pf)
	{
		this->pf = pf;
	}
private:
	A *a;
	pFun pf;

};

int main()
{
	A *a = new A();
	B *b = new B();
	b->SetA(a);
	b->SetPf(A::backcall);
	b->CalledA(10);
	delete a;
	delete b;
}


      在呼叫回撥函式的時候,傳遞給他一個A的物件就可以了訪問非靜態成員或方法了 ,那麼有時候 我們不想把回撥函式設為static 怎麼辦呢,程式碼如下
#include "stdafx.h"
#include <iostream>
using namespace std;
typedef void(*pFun)(int);
class A
{
public:
	friend void BackCall(int m)
	{
		cout << "友元函式被呼叫" << m << endl;
	}

};

void show(pFun f)
{
	f(10);
}


int main()
{
	show(BackCall);
}