小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

C++中類成員函數(shù)作為回調(diào)函數(shù)

 Y忍冬草 2017-07-27

轉(zhuǎn)自:http://blog.csdn.net/this_capslock/article/details/17001003

注:與tr1::function對象結(jié)合使用,能獲得更好的效果,詳情見http://blog.csdn.net/this_capslock/article/details/38564719

回調(diào)函數(shù)是基于C編程的Windows SDK的技術(shù),不是針對C++的,程序員可以將一個C函數(shù)直接作為回調(diào)函數(shù),但是如果試圖直接使用C++的成員函數(shù)作為回調(diào)函數(shù)將發(fā)生錯誤,甚至編譯就不能通過。 

普通的C++成員函數(shù)都隱含了一個傳遞函數(shù)作為參數(shù),亦即“this”指針,C++通過傳遞一個指向自身的指針給其成員函數(shù)從而實現(xiàn)程序函數(shù)可以訪問C++的數(shù)據(jù)成員。這也可以理解為什么C++類的多個實例可以共享成員函數(shù)但是確有不同的數(shù)據(jù)成員。由于this指針的作用,使得將一個CALLBACK型的成員函數(shù)作為回調(diào)函數(shù)安裝時就會因為隱含的this指針使得函數(shù)參數(shù)個數(shù)不匹配,從而導(dǎo)致回調(diào)函數(shù)安裝失敗。

這樣從理論上講,C++類的成員函數(shù)是不能當(dāng)作回調(diào)函數(shù)的。但我們在用C++編程時總希望在類內(nèi)實現(xiàn)其功能,即要保持封裝性,如果把回調(diào)函數(shù)寫作普通函數(shù)有諸多不便。經(jīng)過網(wǎng)上搜索和自己研究,發(fā)現(xiàn)了幾種巧妙的方法,可以使得類成員函數(shù)當(dāng)作回調(diào)函數(shù)使用。

這里采用Linux C++中線程創(chuàng)建函數(shù)pthread_create舉例,其原型如下:

 

int pthread_create( pthread_t *restrict tidp , const pthread_attr_t *restrict attr , void* (*start_rtn)(void*) , void *restrict arg );

 

第一個參數(shù)為指向線程標(biāo)識符的指針。
第二個參數(shù)用來設(shè)置線程屬性。
第三個參數(shù)是線程運行函數(shù)的起始地址,即回調(diào)函數(shù)。
最后一個參數(shù)是運行函數(shù)的參數(shù)。

這里我們只關(guān)注第三個參數(shù)start_run,它是一個函數(shù)指針,指向一個以void*為參數(shù),返回值為void*的函數(shù),這個函數(shù)被當(dāng)作線程的回調(diào)函數(shù)使用,線程啟動后便會執(zhí)行該函數(shù)的代碼。

方法一:回調(diào)函數(shù)為普通函數(shù),但在函數(shù)體內(nèi)執(zhí)行成員函數(shù)
見以下代碼:
class MyClass
{
	pthread_t TID;
public:
	void func()
	{
		//子線程執(zhí)行代碼
	}

	bool startThread()
	{//啟動子線程
		int ret = pthread_create( &TID , NULL , callback , this );
		if( ret != 0 )
			return false;
		else
			return true;
	}
};

static void* callback( void* arg )
{//回調(diào)函數(shù)
	((MyClass*)arg)->func();調(diào)用成員函數(shù)
	return NULL;
}

int main()
{
	MyClass a;
	a.startThread();
}

類MyClass需要在自己內(nèi)部開辟一個子線程來執(zhí)行成員函數(shù)func()中的代碼,子線程通過調(diào)用startThread()成員函數(shù)來啟動。這里將回調(diào)函數(shù)callback寫在了類外面,傳遞的參數(shù)是一個指向MyClass對象的指針(在pthrad_create()中由第4個參數(shù)this指定),回調(diào)函數(shù)經(jīng)過強制轉(zhuǎn)換把void*變?yōu)镸yClass*,然后再調(diào)用arg->func()執(zhí)行子線程的代碼。

這樣做的原理是把當(dāng)前對象的指針當(dāng)作參數(shù)先交給一個外部函數(shù),再由外部函數(shù)調(diào)用類成員函數(shù),以外部函數(shù)作為回調(diào)函數(shù),但執(zhí)行的是成員函數(shù)的功能,這樣相當(dāng)于在中間作了一層轉(zhuǎn)換。缺點是回調(diào)函數(shù)在類外,影響了封裝性,這里把callback()限定為static,防止在其它文件中調(diào)用此函數(shù)。


 

方法二:回調(diào)函數(shù)為類內(nèi)靜態(tài)成員函數(shù),在其內(nèi)部調(diào)用成員函數(shù)

在方法一上稍作更改,把回調(diào)函數(shù)搬到類MyClass里,這樣就保持了封裝性。代碼如下:

 

class MyClass
{
	static MyClass* CurMy;//存儲回調(diào)函數(shù)調(diào)用的對象
	static void* callback(void*);//回調(diào)函數(shù)
	pthread_t TID;
	void func()
	{
		//子線程執(zhí)行代碼
	}
	
	void setCurMy()
	{//設(shè)置當(dāng)前對象為回調(diào)函數(shù)調(diào)用的對象
		CurMy = this;
	}
public:
	bool startThread()
	{//啟動子線程
		setCurMy();
		int ret = pthread_create( &TID , NULL , MyClass::callback , NULL );
		if( ret != 0 )
			return false;
		else
			return true;
	}
};
MyClass* MyClass::CurMy = NULL;
void* MyClass::callback(void*)
{
	CurMy->func();
	return NULL;
}

int main()
{
	MyClass a;
	a.startThread();
}
類MyClass有了1個靜態(tài)數(shù)據(jù)成員CurMy和1個靜態(tài)成員函數(shù)callback。CurMy用來存儲一個對象的指針,充當(dāng)方法一中回調(diào)函數(shù)的參數(shù)arg。callback當(dāng)作回調(diào)函數(shù),執(zhí)行CurMy->func()的代碼。每次建立線程前先要調(diào)用setCurMy()來讓CurMy指向當(dāng)前自己。

 

這個方法的好處時封裝性得到了很好的保護(hù),MyClass對外只公開一個接口startThread(),子線程代碼和回調(diào)函數(shù)都被設(shè)為私有,外界不可見。另外沒有占用callback的參數(shù),可以從外界傳遞參數(shù)進(jìn)來。但每個對象啟動子線程前一定要注意先調(diào)用setCurMy()讓CurMy正確的指向自身,否則將為其它對象開啟線程,這樣很引發(fā)很嚴(yán)重的后果。


 

方法三:對成員函數(shù)進(jìn)行強制轉(zhuǎn)換,當(dāng)作回調(diào)函數(shù)

代碼如下:

 

class MyClass
{
	pthread_t TID;
	void func()
	{
		//子線程執(zhí)行代碼
	}
public:
	bool startThread()
	{//啟動子線程
		typedef void* (*FUNC)(void*);//定義FUNC類型是一個指向函數(shù)的指針,該函數(shù)參數(shù)為void*,返回值為void*
		FUNC callback = (FUNC)&MyClass::func;//強制轉(zhuǎn)換func()的類型
		int ret = pthread_create( &TID , NULL , callback , this );
		if( ret != 0 )
			return false;
		else
			return true;
	}
};

int main()
{
	MyClass a;
	a.startThread();
}
這個方法是原理是,MyClass::func最終會轉(zhuǎn)化成 void func(MyClass *this); 也就是說在原第一個參數(shù)前插入指向?qū)ο蟊旧淼膖his指針。可以利用這個特性寫一個非靜態(tài)類成員方法來直接作為線程回調(diào)函數(shù)。對編譯器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)這兩種函數(shù)指針雖然看上去很不一樣,但他們的最終形式是相同的,因此就可以把成員函數(shù)指針強制轉(zhuǎn)換成普通函數(shù)的指針來當(dāng)作回調(diào)函數(shù)。在建立線程時要把當(dāng)前對象的指針this當(dāng)作參數(shù)傳給回調(diào)函數(shù)(成員函數(shù)func),這樣才能知道線程是針對哪個對象建立的。

 

方法三的封裝性比方法二更好,因為不涉及多個對象共用一個靜態(tài)成員的問題,每個對象可以獨立地啟動自己的線程而不影響其它對象。


 

暫時就列出這些方法,以后發(fā)現(xiàn)更好的再來補充,over




 


 


 


 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多