最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++成員函數(shù)如何當(dāng)作回調(diào)函數(shù)同時(shí)傳遞this指針

 更新時(shí)間:2022年11月25日 09:05:04   作者:易拉罐里的人  
這篇文章主要介紹了C++成員函數(shù)如何當(dāng)作回調(diào)函數(shù)同時(shí)傳遞this指針,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

就我目前了解所知,有三種函數(shù)可以作為回調(diào)函數(shù):

  • 1.普通函數(shù)
  • 2.靜態(tài)函數(shù)(我用得少?zèng)]有寫,直接跳過(guò))
  • 3.成員函數(shù)

1.普通函數(shù)作為注冊(cè)函數(shù)

普通函數(shù)作為回調(diào)函數(shù),比較簡(jiǎn)單,只要函數(shù)簽名(返回值類型+參數(shù)類型)一致就可以了。

因?yàn)槠胀ê瘮?shù)不是類成員函數(shù),如果想要訪問(wèn)類成員,在執(zhí)行回調(diào)函數(shù)的時(shí)候,要把對(duì)象指針傳給回調(diào)函數(shù),如下代碼:

namespace yy0
{
	//普通全局函數(shù)
	void call_back(void* pointer);
 
	class A
	{
	public:
		A() {
			//初始化指針
			p_call_back = NULL;
			//單數(shù)最大的數(shù)
			num = 9;
		}
		~A() {}
	public:
		//打印這個(gè)數(shù)來(lái)驗(yàn)證是否正常調(diào)用回調(diào)函數(shù)
		int num;
	private:
		//指向回調(diào)函數(shù)的地址的指針
		void(*p_call_back)(void*);
 
	public:
		//用于注冊(cè)回調(diào)函數(shù)
		void register_call_back(void(*p)(void*)) {
			if (p)
				p_call_back = p;
		}
 
		//執(zhí)行回調(diào)函數(shù)
		void run_call_back() {
			if (p_call_back)
			{
				//把對(duì)象指針傳遞出去
				p_call_back(this);
			}		
		}
 
		//測(cè)試函數(shù)
		void test() {
			//注冊(cè)
			register_call_back(call_back);
			//執(zhí)行
			run_call_back();
		}
	};
 
	void call_back(void* pointer)
	{
		if (pointer)
		{
			//需要進(jìn)行指針轉(zhuǎn)換
			A* p = (A*)pointer;
			cout << "打印的值:" << p->num;
		}
	}
}
 
 
int main()
{
	yy0::A a;
	a.test();
	getchar();
	return 0;
}

結(jié)果正確打印,說(shuō)明回調(diào)函數(shù)正常調(diào)用:

也可以定義一個(gè)全局的類對(duì)象指針:

namespace yy0
{
	//前置聲明
	class A;
	//普通全局函數(shù)
	void call_back();
	//全局的類對(duì)象指針
	A* pointer = NULL;
	class A
	{
	public:
		A() {
			//給全局類對(duì)象指針賦值
			pointer = this; 
			//初始化指針
			p_call_back = NULL;
			//單數(shù)最大的數(shù)
			num = 9;
		}
		~A() {}
	public:
		//打印這個(gè)數(shù)來(lái)驗(yàn)證是否正常調(diào)用回調(diào)函數(shù)
		int num;
	private:
		//指向回調(diào)函數(shù)的地址的指針
		void(*p_call_back)();
 
	public:
		//用于注冊(cè)回調(diào)函數(shù)
		void register_call_back(void(*p)()) {
			if (p)
				p_call_back = p;
		}
 
		//執(zhí)行回調(diào)函數(shù)
		void run_call_back() {
			if (p_call_back)
			{
				//把對(duì)象指針傳遞出去
				p_call_back();
			}		
		}
 
		//測(cè)試函數(shù)
		void test() {
			//注冊(cè)
			register_call_back(call_back);
			//執(zhí)行
			run_call_back();
		}
	};
 
	
	void call_back()
	{
		if (pointer)
		{
			//需要進(jìn)行指針轉(zhuǎn)換
			A* p = (A*)pointer;
			cout << "打印的值:" << p->num;
		}
	}
}

這也可以正確執(zhí)行,但是這種定義全局的對(duì)象指針有風(fēng)險(xiǎn)。如果只創(chuàng)建一個(gè)A的對(duì)象,就可以正常使用,不會(huì)出現(xiàn)什么太大問(wèn)題。但是,一旦創(chuàng)建的對(duì)象個(gè)數(shù)≥2,那么就造成數(shù)據(jù)讀取錯(cuò)誤的問(wèn)題。

可以想象一下,創(chuàng)建對(duì)象a1時(shí),全局對(duì)象指針pointer是指向a1的位置,那么讀取的pointer->num,是a1對(duì)象的num。

然后再創(chuàng)建a2,那么全局對(duì)象指針pointer就變成了指向a2的位置(因?yàn)閜ointer是個(gè)全局變量,從始至終只有一個(gè)這個(gè)變量),那么執(zhí)行a2.text(),pointer->num讀取的是a2的num。

如果執(zhí)行a1.text(),那么此時(shí),pointer->num讀取的也是a2的num,而不是a1的num。更嚴(yán)重的是,一旦刪除了a1或者a2,就會(huì)造成另外一個(gè)對(duì)象訪問(wèn)內(nèi)存失敗的問(wèn)題。

2.靜態(tài)函數(shù)作為注冊(cè)函數(shù)

這個(gè)就自行上網(wǎng)查看吧,我用的少就不寫了。

3.成員函數(shù)作為注冊(cè)函數(shù)

假設(shè)場(chǎng)景:A類成員函數(shù)作為B類回調(diào)函數(shù)

《深度探索C++對(duì)象模型》這本書講到,類成員函數(shù)都有一個(gè)隱藏參數(shù)用于傳遞this指針,這個(gè)this傳遞給函數(shù)由編譯器來(lái)完成,不需要用戶來(lái)做。

直接上代碼:

namespace yy3
{
 
	class B
	{
	public:
		B() {
			pointer = NULL;
		}
		~B() {}
 
	public:
		//存放A類的this指針
		void* pointer;
		//指向回調(diào)函數(shù)
		void(__stdcall *pCallBack)(void*);
	public:
		/*
		@函數(shù)作用:注冊(cè)回調(diào)
		@輸入?yún)?shù):
		void(*p)(void*)			-- 輸入A類的回調(diào)函數(shù)的地址
		void* p_this			-- 輸入A類的this指針
		*/
        //②
		void register_fun(void(__stdcall *p)(void*), void* p_this) {
			pCallBack = p;
			pointer = p_this;
		}
 
		//執(zhí)行回調(diào)
        //③
		void run_call_back() {
			if (pCallBack)
				pCallBack(pointer);
		}
 
	};
 
 
	class A
	{
	public:
		A() {
			a = 5;
		}
		A(int num) {
			a = num;
		};
		~A() {}
	public:
		//在A類中定義一個(gè)B類的變量
		B b;
		//拿來(lái)測(cè)試的變量
		double a;
 
		//定義聯(lián)合,不知道原理,網(wǎng)上查到的技巧
		union for_callback {
			void(__stdcall *fun_int_c)(void*);
			void (A::*fun_in_class)(void*);
		}fp;
	public:
		//要拿來(lái)注冊(cè)的回調(diào)函數(shù)
		void call_back(void* p) {
			A* pointer = (A*)p;
			//能打印出正確的a值就對(duì)了
			cout << "a:" << pointer->a << endl;
		}
 
		//測(cè)試函數(shù)
        //①
		void test() {
			fp.fun_in_class = &A::call_back;
			b.register_fun(fp.fun_int_c, this);
			b.run_call_back();
		}
 
	};
}
 
int main()
{
	yy3::A a;
	a.test();
	getchar();
	return 0;
}

首先來(lái)解釋一地方

1.__stdcall聲明:這個(gè)看情況,我在公司電腦寫的時(shí)候不需要加這個(gè)關(guān)鍵字,自己的電腦就要加這個(gè)。就是一個(gè)傳參約定,可以上網(wǎng)查。

2.

//定義聯(lián)合,不知道原理,網(wǎng)上查到的技巧
union for_callback {
    void(__stdcall *fun_int_c)(void*);
    void (A::*fun_in_class)(void*);
}fp;

使用union,這個(gè)說(shuō)是為了逃避編譯器檢查,原理我也不太懂,如果有知道原理的大神,麻煩告訴一下下,感謝感謝。我直接就拿來(lái)用了。

3.前面說(shuō)了成員函數(shù)有個(gè)隱含傳遞指針的參數(shù),所以函數(shù)指針:

//指向回調(diào)函數(shù)
void(__stdcall *pCallBack)(void*);

需要定義參數(shù)為void*的函數(shù)指針,用于傳遞A類的this指針

4.因?yàn)楹瘮?shù)指針是B類的成員,而函數(shù)指針接受的參數(shù)是A類的this指針,我們不能直接這樣使用:

void run_call_back() {
	if (pCallBack)
		pCallBack(this);
}

這個(gè)pCallBack(this)中的this是指向B類對(duì)象的地址而非A類對(duì)象的地址,因此,在B類定義一個(gè)成員:void* pointr,用于保存A類對(duì)象的指針,然后這樣使用

//執(zhí)行回調(diào)
void run_call_back() {
	if (pCallBack)			
    pCallBack(pointer);
}

這樣就運(yùn)行回調(diào)函數(shù),同時(shí)傳遞A類對(duì)象指針。

5.(無(wú)參這一點(diǎn)單獨(dú)在這里說(shuō))當(dāng)然,雖然成員函數(shù)有自帶隱藏參數(shù),我們也可以把它轉(zhuǎn)換成無(wú)參的函數(shù),修改這些地方:

//【1】
//指向回調(diào)函數(shù)
void(__stdcall *pCallBack)(void*);
//修改為
void(__stdcall *pCallBack)();
 
//【2】
void register_fun(void(__stdcall *p)(void*), void* p_this) {
	pCallBack = p;
	pointer = p_this;
}
//修改為
void register_fun(void(__stdcall *p)(), void* p_this) {
	pCallBack = p;
	pointer = p_this;
}
 
//【3】
//執(zhí)行回調(diào)
void run_call_back() {
	if (pCallBack)
		pCallBack(pointer);
}
//修改為
void run_call_back() {
	if (pCallBack)
		pCallBack();
}
 
//【4】
union for_callback {
	void(__stdcall *fun_int_c)(void*);
	void (A::*fun_in_class)(void*);
}fp;
//修改為
union for_callback {
	void(__stdcall *fun_int_c)();
	void (A::*fun_in_class)();
}fp;
 
//【5】
//要拿來(lái)注冊(cè)的回調(diào)函數(shù)修改為
void call_back() {
	cout << "a:" << this->a << endl;
}

這種情況編譯能通過(guò),但是void call_back()使用this指針,是無(wú)法正確讀取內(nèi)存的值,如下

言歸正傳。

成員函數(shù)轉(zhuǎn)為帶一個(gè)void*參數(shù)的函數(shù)運(yùn)行情況如下:

 結(jié)果也是一個(gè)不正確的值,因此進(jìn)行調(diào)試查看,把斷點(diǎn)放在這個(gè)函數(shù)上,發(fā)現(xiàn)了一個(gè)奇怪的問(wèn)題:

		//要拿來(lái)注冊(cè)的回調(diào)函數(shù)
		void call_back(void* p) 
		{
			A* pointer = (A*)p;
			//能打印出正確的a值就對(duì)了
			cout << "a:" << pointer->a << endl;	
		}

pointer是A類對(duì)象的指針,pointer通過(guò)函數(shù)指針pCallBack(pointr)傳遞給了call_back(void* p),從理論上講,p的值要與pointer保持一致才對(duì)。但是p的值與pCaalBack相同,也就是p是函數(shù)指針,特別奇怪。我也不知道什么原因,所以如果有人知道,麻煩跟我講一下,在這里先謝謝了。

我無(wú)法解決這個(gè)問(wèn)題,所以嘗試了將函數(shù)指針轉(zhuǎn)為帶有兩個(gè)void*參數(shù)的函數(shù),竟然可以傳遞正確的this指針,算是瞎貓碰上死耗子,代碼跟上面類似,如下:

namespace yy3
{
 
	class B
	{
	public:
		B() {
			pointer = NULL;
		}
		~B() {}
 
	public:
		//存放A類的this指針
		void* pointer;
		//指向回調(diào)函數(shù)
		void(__stdcall *pCallBack)(void*, void*);
	public:
		/*
		@函數(shù)作用:注冊(cè)回調(diào)
		@輸入?yún)?shù):
		void(*p)(void*,void*)	-- 輸入A類的回調(diào)函數(shù)的地址
		void* p_this			-- 輸入A類的this指針
		*/
		void register_fun(void(__stdcall *p)(void*, void*), void* p_this) {
			pCallBack = p;
			pointer = p_this;
		}
 
		//執(zhí)行回調(diào)
		void run_call_back() {
			if (pCallBack)
				//需要兩個(gè)指針作為參數(shù),干脆就傳遞兩個(gè)pointer吧
				pCallBack(pointer,pointer);
		}
 
	};
 
 
	class A
	{
	public:
		A() {
			a = 5;
		}
		A(int num) {
			a = num;
		};
		~A() {}
	public:
		//在A類中定義一個(gè)B類的變量
		B b;
		//拿來(lái)測(cè)試的變量
		double a;
 
		//定義聯(lián)合,不知道原理,網(wǎng)上查到的技巧
		union for_callback {
			void(__stdcall *fun_int_c)(void*, void*);
			void (A::*fun_in_class)(void*, void*);
		}fp;
	public:
		//要拿來(lái)注冊(cè)的回調(diào)函數(shù)
		void call_back(void* p, void* pp)
		{
			A* pointer = (A*)p;
			//能打印出正確的a值就對(duì)了
			cout << "a:" << pointer->a << endl;	
		}
 
		//測(cè)試函數(shù)
		void test() {
			fp.fun_in_class = &A::call_back;
			b.register_fun(fp.fun_int_c, this);
			b.run_call_back();
		}
 
	};
}

結(jié)果是正確的:

 從圖上可知,pCallBack(函數(shù)指針)的值,與p和pp都不同,無(wú)論是p還是pp,這兩個(gè)值都是A類對(duì)象的地址,也就是說(shuō),已經(jīng)成功把A的this指針傳遞進(jìn)來(lái)了。因此結(jié)果也是正確的。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • OpenCV實(shí)現(xiàn)圖像切割功能

    OpenCV實(shí)現(xiàn)圖像切割功能

    這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)圖像切割功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • 一問(wèn)學(xué)會(huì)QT時(shí)間類

    一問(wèn)學(xué)會(huì)QT時(shí)間類

    QT獲取時(shí)間的類有3個(gè),分別是QDate、QTime、QDateTime,他們屬于QT的network模塊。本文詳細(xì)的介紹了這3個(gè)模塊的使用,感興趣的可以了解一下
    2023-04-04
  • C語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓

    C語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓

    數(shù)據(jù)壓縮是通過(guò)一系列的算法和技術(shù)將原始數(shù)據(jù)轉(zhuǎn)換為更緊湊的表示形式,以減少數(shù)據(jù)占用的存儲(chǔ)空間,數(shù)據(jù)解壓縮則是將壓縮后的數(shù)據(jù)恢復(fù)到原始的表示形式,本文給大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)壓縮與解壓,需要的朋友可以參考下
    2023-08-08
  • C++使用chrono庫(kù)處理日期和時(shí)間的實(shí)現(xiàn)方法

    C++使用chrono庫(kù)處理日期和時(shí)間的實(shí)現(xiàn)方法

    C++11 中提供了日期和時(shí)間相關(guān)的庫(kù) chrono,通過(guò) chrono 庫(kù)可以很方便地處理日期和時(shí)間,本文主要介紹了C++使用chrono庫(kù)處理日期和時(shí)間的實(shí)現(xiàn)方法,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 簡(jiǎn)單講解哈希表

    簡(jiǎn)單講解哈希表

    本文主要介紹了哈希表簡(jiǎn)單知識(shí)及C語(yǔ)言實(shí)現(xiàn)哈希表實(shí)例,文中利用圖片以及代碼簡(jiǎn)單講解了相關(guān)知識(shí),感興趣的小伙伴可以多多學(xué)習(xí)這篇文章
    2021-09-09
  • C語(yǔ)言編寫五子棋游戲

    C語(yǔ)言編寫五子棋游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言編寫五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • C語(yǔ)言基礎(chǔ)知識(shí)分享續(xù)篇

    C語(yǔ)言基礎(chǔ)知識(shí)分享續(xù)篇

    這篇文章主要介紹了C語(yǔ)言基礎(chǔ)知識(shí)分享續(xù)篇的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • C++ Primer Plus 第四章之C++ Primer Plus復(fù)合類型學(xué)習(xí)筆記

    C++ Primer Plus 第四章之C++ Primer Plus復(fù)合類型學(xué)習(xí)筆記

    數(shù)組(array)是一種數(shù)據(jù)格式,能夠存儲(chǔ)多個(gè)同類型的值。每個(gè)值都存儲(chǔ)在一個(gè)獨(dú)立的數(shù)組元素中,計(jì)算機(jī)在內(nèi)存中依次存儲(chǔ)數(shù)組的各個(gè)元素,今天給大家重點(diǎn)介紹C++ Primer Plus復(fù)合類型的實(shí)例詳解,感興趣的朋友一起看看吧
    2021-07-07
  • C++深入講解namespace與string關(guān)鍵字的使用

    C++深入講解namespace與string關(guān)鍵字的使用

    namespace命名空間或者叫名字空間,傳統(tǒng)的c++只有一個(gè)全局的namespace,namespace引入了復(fù)雜性。namespace允許像類,對(duì)象,函數(shù)聚集在一個(gè)名字下。本質(zhì)上講namespace是對(duì)全局作用域的細(xì)分
    2022-05-05
  • C語(yǔ)言壓縮文件和用MD5算法校驗(yàn)文件完整性的實(shí)例教程

    C語(yǔ)言壓縮文件和用MD5算法校驗(yàn)文件完整性的實(shí)例教程

    這篇文章主要介紹了C語(yǔ)言壓縮文件和用MD5算法校驗(yàn)文件完整性的實(shí)例教程,這里演示了Windows下將文件壓縮為7z格式以及MD5檢驗(yàn)文件和密碼的方法,需要的朋友可以參考下
    2016-04-04

最新評(píng)論

若羌县| 多伦县| 富蕴县| 济阳县| 巨鹿县| 睢宁县| 萍乡市| 建瓯市| 广德县| 甘德县| 积石山| 子洲县| 墨竹工卡县| 鹰潭市| 海伦市| 澜沧| 上杭县| 邵东县| 建宁县| 海城市| 裕民县| 雅安市| 吴江市| 甘孜| 会泽县| 鹤壁市| 金华市| 青河县| 光泽县| 合水县| 临清市| 方城县| 五莲县| 灌云县| 遂溪县| 搜索| 东至县| 怀化市| 九龙城区| 钦州市| 乡城县|