回调函数

五种将c++的成员函数用作回调函数的方法——史上最全、最简!!! 前言 在写ROS的cpp代码时发现回调函数十分的常用,然后初学的时候没有深入研究过,记录下几种用法,据说在写线程的时候也很常用。方法五应该是最简单实用的了… 方法一 先从最简单的入手,直接使用普通函数用作回调函数。 #include "pch.h" #include <iostream> using namespace std; class TestCallback { public:... 阅读详情
如何做类的回调函数 前些日子用一个PIPE类封装了WINDOWS的录音放音设备,程序写得有点类似与操作系统的PV信号互锁机制,这里面需要将辅助录音现程采集到的 数据存储到Buffer,然后做一个回掉函数做处理,以下是几种实现回掉的方法,程序最后使用了THUNK机制,再说明这种机制之前。先澄清一下回掉函数 的概念。 所谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。 一般说来,C不会自己调用B,C提供B的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的B叫甚名谁,所以S会约定B的接口规范(函数原型),然后由C提前通过S的一个函数R告诉S自己将要使用B函数,这个过程称为回调函数的注册,R称为注册函数。 1。传统的CALLBACK 传统SDK回调函数设计模式 Win32 SDK是这方面的典型例子,这类SDK的函数接口都是基于C语言的,SDK或者提供专门的注册函数,用于注册回调函数的地址,或者是在调用某个方法时才传 入回调函数的地址,回调函数的原型也由于注册函数中的函数指针定义而受到约束。 以Win32中的多媒体定时器函数为例,其原型为: MMRESULT timeSetEvent( UINT uDelay, // 定时器时间间隔,以毫秒为单位 UINT uResolution, LPTIMECALLBACK lpTimeProc, // 回调函数地址 DWORD dwUser, // 用户设定的数据 UINT fuEvent ); typedef void (CALLBACK TIMECALLBACK)(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); typedef TIMECALLBACK FAR *LPTIMECALLBACK; 因此,用户定义的回调函数必须具有上面指定的函数原型,下面是回调函数的具体使用方法: #include "stdio.h" #include "windows.h" #include "mmsystem.h" // 多媒体定时器需要包含此文件 #pragma comment(lib, "winmm.lib") // 多媒体定时器需要导入此库 void CALLBACK timer_proc(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) // 定义回调函数 { printf("time out./n"); } int main() { UINT nId = timeSetEvent( 1000, 0, timer_proc, 0, TIME_CALLBACK_FUNCTION|TIME_PERIODIC); // 注册回调函数 getchar(); timeKillEvent( nId ); return 0; } 2。简单的实现类成员函数做CALLBACK的方法 C++出现后,CALLBACK函数提出了自身的局限性,因为CALLBACK函数是为C语言的函数设计的,而C++本身的类的this指针导致了回调C++的类成员函数不能得到其有效地址,解决这个局限其实也简单,基本方法有两种 1. 不使用成员函数,直接使用普通C函数,为了实现在C函数中可以访问类的成员变量,可以使用友元操作符(friend),在C++中将该C函数说明为类的友元即可。这种处理机制与普通的C编程中使用回调函数一样。 2. 使用静态成员函数,静态成员函数不使用this指针作为隐含参数,这样就可以作为回调函数了。静态成员函数具有两大特点:其一,可以在没有类实例的情况下 使用;其二,只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数。由于在C++中使用类成员函数作为回调函数的目的就是为了访 问所有的成员变量和成员函数,如果作不到这一点将不具有实际意义。解决的办法也很简单,就是使用一个静态类指针作为类成员,通过在类创建时初始化该静态指 针,如pThis=this,然后在回调函数中通过该静态指针就可以访问所有成员变量和成员函数了。这种处理办法适用于只有一个类实例的情况,因为多个类 实例将共享静态类成员和静态成员函数,这就导致静态指针指向最后创建的类实例。为了避免这种情况,可以使用回调函数的一个参数来传递this指针,从而实 现数据成员共享。这种方法稍稍麻烦,这里就不再赘述。 3 将类成员函数写成虚函数,直接访问虚函数表,得到函数地址,再传递给调用函数 (wang2012629@126.com 的 方法),简单介绍如下 比如说DialogBox(...); 例子如下: // TestCallbackDlg.h : header file class CTestCallbackDlg : public CDialog { // Construction public: virtual LRESULT DlgProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);//放在最前面. CTestCallbackDlg(CWnd* pParent = NULL); // standard constructor// TestCallbackDlg.cpp : implementation file // LRESULT CTestCallbackDlg::DlgProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { TRACE("DlgProc %d/r/n",hWnd); if( wParam ==IDOK) { //AfxMessageBox("gfhdfhfgh");/*前后都不响应,MessageBox不消失?*/ ::MessageBox(hWnd,"HAHAHA","dfgdfgh",MB_OK); ::EndDialog(hWnd,IDOK); //AfxMessageBox("gfhdfhfgh"); } break; } case WM_CLOSE: { AfxMessageBox("gfhdfhfgh"); ::EndDialog(hWnd,IDOK); break; } } return ::DefWindowProc(hWnd, message, wParam, lParam); } //从虚函数表中获取函数地址. void CTestCallbackDlg::OnOK() { // TODO: Add extra validation here unsigned long pThis = (unsigned long) this; DLGPROC TempFuncAddr = NULL; _asm { mov eax,pThis; mov edx,dword ptr [eax]; mov eax, dword ptr [edx+0D8h]; mov TempFuncAddr,eax; } TRACE("CTestCallbackDlg %d/r/n",this->m_hWnd); //TempFuncAddr中就是函数LRESULT CTestCallbackDlg::DlgProc(...)的地址 DialogBox(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), this->m_hWnd, (DLGPROC)TempFuncAddr); } 4。利用抽象类CWndProc //wndpro.h #ifndef __WNDPROC_H__ #define __WNDPROC_H__ class CWndProc { protected: //保护的构造函数,必须由派生类来构造。 CWndProc(); virtual ~CWndProc(); protected: //窗口回调过程,基类作为纯虚函数没有实现代码。 virtual LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0; private: //Hook代码块。 char m_hook[40]; protected: //m_pfnWndProc指针指向Hook代码块的始地址。 //注册窗口类(WNDCLASSEX),或者子类化控件窗口,或者DialogBox显示对话框 //等需要窗口回调过程参数时,使用m_pfnWndProc作为参数。 WNDPROC m_pfnWndProc; }; #endif //__WNDPROC_H__ //end of file 二、实现代码文件 //wndproc.cpp #include "stdafx.h" #include "wndproc.h" /* 全局的Hook代码,其C的伪代码为: LRSULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return (CWndProc派生类的this指针)->WndProc(hwnd, uMsg, wParam, lParam); } 代码的功能就是直接转调用CWndProc派生类的WndProc。 */ static unsigned char g_hook[] = { 0x8B, 0x44, 0x24, 0x10, // mov eax,dword ptr [esp+10h] ; eax <- lParam 0x8B, 0x4C, 0x24, 0x0C, // mov ecx,dword ptr [esp+0Ch] ; ecx <- wParam 0x8B, 0x54, 0x24, 0x08, // mov edx,dword ptr [esp+8] ; edx <- uMsg 0x50, // push eax ; lParam 参数入栈 0x8B, 0x44, 0x24, 0x08, // mov eax,dword ptr [esp+8] ; eax <- hwnd 0x51, // push ecx ; wParam 参数入栈 0xB9, 0x00, 0x00, 0x00, 0x00, // mov ecx,0 ; ecx <- this指针,这里暂时用this(NULL), // ; 在类构造函数初始化时修改为实际类的this指针值 0x52, // push edx ; uMsg 参数入栈 0x50, // push eax ; hwnd 参数入栈 0x51, // push ecx ; this 参数入栈 0xE8, 0x00, 0x00, 0x00, 0x00, // call WndProc ; 调用派生类的WndProc,这暂时用0, // ; 在类构造函数初始化时修改为实际类虚拟表WndProc指针偏移值 0xC2, 0x10, 0x00 // ret 10h ; return }; CWndProc::CWndProc() { char *p; LRESULT (CALLBACK CWndProc::*pfn)(HWND, UINT, WPARAM, LPARAM); CopyMemory(m_hook, g_hook, sizeof(g_hook)); //把全局的Hook代码块,拷贝到类对象的Hook代码块 p = m_hook + 19; //p指针指向 mov ecx, 0 处,以便修改this(NULL)指针为实际类对象的this指针 *((unsigned int *)p) = (unsigned int)this; //修改p所指向的位置为mov ecx, (指向实际的类对象的this指针) pfn = WndProc; //pfn指向类虚拟表中WndProc函数的指针; p = m_hook + 27; //p指针指向 call WndProc处,以便修改WndProc虚表指针相对偏移值 //由于vc6.0无法修改pfn及强制其类型,所以下面使用几句汇编 __asm { mov eax, pfn ; eax <- pfn sub eax, 4 ; eax <- eax-4 mov edi, p ; edi <- p指针 sub eax, edi ; eax <- eax-edi 计算WndProc虚表指针与当前 EIP+5 相对偏移值 mov [edi], eax ; eax <- [edi] 修改p所指向的位置为 call (WndProc虚表指针与当前 EIP+5 相对偏移值) } m_pfnWndProc = (WNDPROC)&m_hook[0]; //把Hook代码块始址赋给m_pfnWndProc } CWndProc::~CWndProc() { } 五。导入类指针地址作为成员函数参数typedef void (*HELP_CALLBACK)(const char*, unsigned long); // 回调函数指针 void help_me( const char* question, HELP_CALLBACK callback, unsigned long user_value ); // 接口声明 #endif//__SDK_H__ #include "sdk.h" #include "stdio.h" #include "windows.h" HELP_CALLBACK g_callback; unsigned long g_user_value; void do_it() { printf("thinking.../n"); Sleep( 3000 ); printf("think out./n"); printf("call him./n"); g_callback( "2.", g_user_value ); // 将用户设定的数据传入 } void help_me( const char* question, HELP_CALLBACK callback, unsigned long user_value ) { g_callback = callback; g_user_value = user_value; // 保存用户设定的数据 printf("help_me: %s/n", question); do_it(); } /// app.cpp (应用程序源文件) #include "sdk.h" #include "stdio.h" #include "assert.h" class App { public: App( const char* name ) : m_name(name) { } void ask( const char* question ) { help_me( question, got_answer, (unsigned long)this ); // 将this指针传入 } static void got_answer( const char* msg, unsigned long user_value ) { App* pthis = (App*)user_value; // 转换成this指针,以访问非静态数据成员 assert( pthis ); printf("%s got_answer: %s/n", pthis->m_name, msg); } protected: const char* m_name; }; int main() { App app("ABC"); app.ask( "1+1=?"); return 0; } 六:最酷的方法,THUNK机制(感谢北理论坛的detrox ,这个方法太棒了) 在VC++中,所有类的成员函数在定义的时候都被隐式(implicit)定义为__thiscall参数传递方式。在MSDN 中对__thiscall做了如下定义: 引用: The __thiscall calling convention is used on member functions and is the default calling convention used by C++ member functions that do not use variable arguments. Under __thiscall, the callee cleans the stack, which is impossible for vararg functions. Arguments are pushed on the stack from right to left, with the this pointer being passed via register ECX, and not on the stack, on the x86 architecture. 其中心思想是,__thiscall 修饰的函数参数从右至左依次压入堆栈,被调用者负责平衡堆栈。之后是与C语言所有参数传递方式均不相同的一点:成员函数所在类的this指针被存入ecx寄存器(这个特性只针对Intel x86架构)。 对比之后,我们发现类成员函数不能作为回调函数的主要原因在于类成员函数使用__thiscal参数传递方式,因此需要调用者(caller)通过ecx寄存器提供类对象的指针。而回调函数使用__stdcall参数传递方式,不具备这个特点。 如何让类成员函数成为回调函数 ------------------------------------------------------------------ 根据第一节对回调函数与类成员函数各自特点的分析。不难发现,只要能想办法在类成员函数被调用之前设置好ecx寄存器,就能在__stdcall调用的基础上模拟出一个完好的__thiscall调用。 如 何提前设置ecx寄存器呢?我们知道函数调用实际是通过汇编指令(oprand)’call 函数地址’完成的。因此我们可以提供一个中间函数。当回调发生时,先调用中间函数,再在中间函数执行过程中设置ecx寄存器,当ecx设置好后jmp到类 成员函数去(注意:这里是jmp不是call)。当执行到类的成员函数时,函数上下文(function context)就和__thiscall所产生的完全一样了。 如何制作这个中间函数呢?普通的函数是不行的。主要因为在vc++ debug版本的代码中要使用ecx寄存器做堆栈溢出检测(stack overflow detect),即使是空函数都是如此。其次由于存在栈框(stack frame)效率也不高。 这时就需要使用thunk来达到我们的目的。所谓thunk就是程序自己生成并执行的一小段汇编代码。下面通过代码来理解thunk。 thunk实现: 引用: 以下代码在Windows XP SP2, Visual Studio 2005 Team Suite 下调试成功 所用处理器 Intel Pentium M 750, Dothan Core cpp 代码 [复制到剪贴板]#include "windows.h" #include "stdio.h" #include "stdlib.h" #include "assert.h" #include "stdafx.h" ////////////////////////////////////////////////////////////////////////// // 回调函数类型定义 typedef int (CALLBACK *pfaCallBack)(int, long, char); ///////////////////////////////////////////////////////////////////////// // thunk 结构定义 // 由于thunk 要被当作代码来执行,因此thunk 结构必须是字节对齐的,这里使用 // VC++ 的修饰符号#pragma pack(push, 1) 来定义一个字节对齐的结构体 // 之后通过#pragma(pop) 恢复默认对齐模式 #pragma pack(push, 1) struct Thunk { BYTE op_movecx; DWORD_PTR val_ecx; BYTE op_call; DWORD_PTR val_address; }; #pragma pack(pop) ////////////////////////////////////////////////////////////////////////// // 一个类的定义,就这样平静的开始了 class Dummy { // 一个成员变量 private: int m_id ; // 定义一个thunk private: Thunk m_thunk; // 定义构造函数,在构造函数中设置m_id值 public: Dummy(int id):m_id(id) { } ///////////////////////////////////////////////////////////////////////// // 定义一个回调函数,另外他还是个类的成员函数呢 public: int memberCallback(int intVal, long longVal, char charVal) { // 做自己想做的事情 printf("/nI am a member function of class Dummy" "(Dummy::memberCallback),ID = %d." "/nI got the value 0x%08x 0x%08x /'%c/'" , m_id, intVal, longVal, charVal); return m_id; } ////////////////////////////////////////////////////////////////////////// // 初始化thunk 的数据,这里是关键 public: void InitThunk() { // 0xB9是'mov ecx, 数值’的机器码,xB9之后的个字节(32位)指定了要 // 给ecx的数值. m_thunk.op_movecx = 0xB9; // 填写要给ecx的数值为this(类对象的指针) m_thunk.val_ecx = (DWORD_PTR)this; // 0xE9是'jmp 相对地址’的机器码。相对地址由xE9之后的个字节(32位) // 给出。 m_thunk.op_call = 0xE9; // 获得Dummy::memberCallback的具体地址。关于成员函数与类对象的关系 // 请参阅Stan Lippman 的<> // 用汇编获得地址省去了用C++带来的难看的语法 DWORD_PTR off = 0; _asm { mov eax, Dummy::memberCallback mov DWORD PTR [off], eax } // jmp后面是相对地址,因此要求出这个地址 // 相对地址=成员函数地址-跳转点下一指令地址 // 正负号不要紧,jmp自己能根据正负判断如何跳。 m_thunk.val_address = off - ( (DWORD_PTR)(&m_thunk.val_address) + sizeof(DWORD_PTR) ); } ////////////////////////////////////////////////////////////////////////// // 返回thunk的地址给要回调他的函数。 // 那个函数还以为thunk是一个函数地址呢。根本不知道thunk是我们自己构造的 // 数据 public: pfaCallBack GetStaticEntry() { return (pfaCallBack)&m_thunk; } }; ////////////////////////////////////////////////////////////////////////// // 一个调用回调函数的函数 void Trigger(pfaCallBack callback) { assert(callback); int intVal = 0x1234; int longVal = 0x5678ABCD; int charVal = 'D'; // 函数内部 int r; // 开始回调 r = callback(intVal, longVal, charVal); printf("/n Return value = %d/n", r); } ////////////////////////////////////////////////////////////////////////// // 传说中的主函数。VC++工程里生成的就叫_tmain不叫main。 int _tmain(int argc, _TCHAR* argv[]) { //生成一个对象 Dummy *dummy1 = new Dummy(9); //初始化thunk dummy1->InitThunk(); //取得thunk地址 pfaCallBack pCallback1 = dummy1->GetStaticEntry(); //给需要回调函数的函数传递thunk Trigger(pCallback1); // 按任意键继续... system("pause"); return 0; } cpp 代码 pragma once #include #include // BYTE Alignment #pragma pack(push, 1) struct ThunkData { struct { BYTE opcode; DWORD_PTR oprand; } movecx; struct { BYTE opcode; DWORD_PTR oprand; } jmp; }; #pragma pack(pop) template class CThunk { public: CThunk(); public: ~CThunk(void); public: void Attach(DWORD_PTR owner, PointerType PointerToMemberFunction); public: void * GetThunk() { assert(bAttached == true);return &m_thunkData;}; public: operator CallbackType() {return (CallbackType)(&m_thunkData);}; private: PointerType m_fPointer; private: ThunkData m_thunkData; private: bool bAttached; }; template CThunk::CThunk(): bAttached(false) { // make sure that this is stored in ecx #ifdef _DEBUG DWORD_PTR pthis; _asm { mov dword ptr [pthis],ecx }; assert(pthis == (DWORD_PTR)this); #endif // mov ecx, this m_thunkData.movecx.opcode = 0xB9; m_thunkData.jmp.opcode = 0xE9; } template CThunk::~CThunk(void) { } template void CThunk::Attach(DWORD_PTR owner, PointerType PointerToMemberFunction) { assert(owner); assert(PointerToMemberFunction); m_fPointer = PointerToMemberFunction; DWORD_PTR FunctionOffset; _asm { mov eax, PointerToMemberFunction mov FunctionOffset, eax } m_thunkData.jmp.oprand = FunctionOffset - ( (DWORD_PTR)&m_thunkData.jmp.oprand + sizeof(DWORD_PTR) ); m_thunkData.movecx.oprand = owner; bAttached = true; } 使用方法 cpp 代码 [ class Dummy { private: int m_id ; ////////////////////////////////////////////////////////////////////////// // Dummy Thunk public: CThunk DummyThunk; CThunk DummyThunk2; ////////////////////////////////////////////////////////////////////////// // Default constructor public: Dummy():m_id(0) { DummyThunk.Attach((DWORD_PTR)this, &Dummy::memberCallback); DummyThunk2.Attach((DWORD_PTR)this, &Dummy::memberCallback2); } .... ... Trigger(dummy1->DummyThunk); Trigger((pfaCallBack)dummy2->DummyThunk.GetThunk()); Trigger(dummy3->DummyThunk2); ... cpp 代码 #define THUNK(NAME,FUNCTION) private: ThunkData m_##NAME##THUNK;/ public:/ void * NAME##Thunk() {/ DWORD_PTR dwptr;/ m_##NAME##THUNK.oprMOV = (DWORD_PTR)this;/ __asm {mov eax, FUNCTION}/ __asm {mov dwptr, eax}/ m_##NAME##THUNK.oprJMP = dwptr - (((DWORD_PTR)&m_##NAME##THUNK.oprJMP) + sizeof(DWORD_PTR));/ return &m_##NAME##THUNK;/ };/ 使用方法 class Dummy { THUNK(memberCallback, Dummy::memberCallback); THUNK(AnotherCallback, Dummy::AnotherCallback); private: int m_id ; .... cpp 代码 Trigger((pfaCallBack)dummy1->memberCallbackThunk()); Trigger((pfaCallBack)dummy2->AnotherCallbackThunk()); 转自 黄申的博客 http://blog.sina.com.cn/qqhuangshen
VC++开发的虚拟键盘程序源码实战项目 在虚拟键盘项目中,最直观的应用就是将每个按键抽象成一个KeyButton。该不仅包含描述其外观的属性(如标签文本、颜色、坐标),还封装了与其行为相关的操作方法(如绘制、响应点击、触发消息)。这种方式避免了传统过程式编程中全局变量泛滥和函数调用混乱的问题。private:// 按键显示文本// 屏幕坐标矩形// 正常状态背景色// 被按下时的颜色// 当前是否处于按下状态public:// 绘制按键// 判断点是否落在按键区域内// 鼠标按下处理// 鼠标释放处理。 阅读详情

相关推荐

C++将的成员函数作为回调函数

C++将的成员函数作为回调函数使用介绍

C/C++中可变参数的原理 与函数调用约定

今天面试比较悲剧,补一补基础知识吧。 这是Richard Wei的文章,大家去看:http://www.cnblogs.com/weiym/archive/2012/09/18/2689917.html C/C++中可变参数的原理 以前只是知道可变参数怎么用,但是一直对它的原理是似懂非懂,现在对计算机有了比较深刻的认识之后,回头再看,豁然开朗。 要理解可变参数,首先要理解函

兵临城下 3261

成员函数回调函数的方法

普通的C++成员函数都隐含了一个“this”指针参数,当在的非静态成员函数中访问的非静态成员时,C++编译器通过传递一个指向对象本身的指针给其成员函数,从而能够访问的数据成员。也就是说,即使你没有写上this指针,编译器在编译的时候自动加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。在创建线程调用回调函数时,传入对象的指针(比如this指针)作为参数,并在回调函数中把void*强制转换为的指针(MyClass*),就能使用该指针调用的成员函数

jinking01的专栏 1577

C++回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。 回调函数机制: 1、定义一个函数(普通函数即可); 2、将此函数的地址注册给调用者; 3、特定的事件或条件发生时,调用者使用函数指针调用回调函数。 C回调函数 C++回调函数扩展自C回调函数,要想理解C++回调函数,先要理解C回调函数。我们通过

lovely_yoshino的博客 6956

【c++】16.回调函数、注册回调函数

回调函数机制 使用了 函数指针。 1.定义一个函数指针,如typedef void (*func_ptr)(std::string str); 2.定义一个 回调函数 void func(std::string str),需要注意的是,回调函数必须和前面定义的函数指针参数型和参数个数一致 3.定义一个注册回调函数 void SetCallback(func_ptr ptr);, 其中 func_ptr 是一个函数指针型,之后会把一个回调函数地址(函数名func)赋值给 ptr; func_ptr m_p

u011754972的博客 8553

回调函数的实质——什么是回调函数,为什么要使用回调函数

在C/C++里面,函数的概念很好理解,就是把某个任务独立出来,封装在一起,然后给它取个名字,它可以有参数和返回值。基本上跟数学里的函数概念一致。那么,回调函数是个什么鬼呢?它和函数到底有何异同?既然已经有了函数,为啥还非要生出个回调函数来?想必,小伙伴们在刚碰到这个概念的时候,都会被这些问题困扰。网上搜一搜,有很多相关的材料,但是未必透彻。我觉得要真正理解一个概念,必须要先理解它存在的意义,也就是

操练起来。。。 3万+

C++回调函数以及epoll中回调函数的使用

回调函数是一种常用的编程技术,它允许程序在运行时将一个函数作为参数传递给另一个函数,以实现更加灵活和可扩展的功能。在C++中,回调函数通常被实现为函数指针或者函数对象。函数指针是指向函数的指针变量,可以通过它来调用函数函数对象是一种可调用对象,它是一个的对象,其中定义了一个operator()函数,可以像普通函数一样进行调用。在上面的示例中,我们定义了一个回调函数Callback,该型是一个函数指针,接受一个int型的参数并返回void。

ZZU_LGY 5599

回调函数回调函数回调函数,什么是回调函数

这是一个关于回调函数的全面讲解

weixin_51205269的博客 623

Qt中回调函数的实现

Qt中回调函数的实现 文章目录Qt中回调函数的实现一、关于回调函数的介绍二、回调函数使用的实列1.调用回调函数2.回调函数实现总结 一、关于回调函数的介绍 这个关于回调函数的介绍可以查看知乎上关于回调函数的介绍通俗易懂 回调函数callback)是什么. 上面的回答让我理解回调函数的还是[A 让 B 排序,B 会做排序,但排序需要知道哪个比哪个大,这点 B 自己不知道,就需要 A 告诉它。而判断大小本身是某种行为,既然 C 语言中不可以传进第一值的函数,就设计成传递第二值的函数指针,这个函数指针是

sqlxby的博客 8273

回调函数详解

回调函数

m0_60237095的博客 2632

delphi 回调函数

       今天在DELPHI上随便编写了个回调函数的例子,怕以后忘了,赶紧给它给记下来,呵呵。觉的好简单,什么都没有,只是在同一个单元内,利用了DELPHI的多线程调用回调函数,高手别见笑哈!下次肯定要多加点东西完善滴,这次只是熟悉一下实现回调的整个过程。interfaceuses  Windows, Messages, SysUtils, Variants, Classes,

╰☆斌☆╮涂鸦 7150

什么是回调函数

1 定义和使用场合 回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。 这一设计允许了底层代码调用在高层定义的子程序(如图1-1所...

往事撩人醉的博客 6万+

ajax五种回调函数,Ajax的回调函数

一,为什么要使用回调函数?首先我们应该明白我们为什么要用回调函数?我们用它能解决什么样的问题。function d(){}(function(){(function(){var temp ="this is a test";})();})();(1) 在以上函数d中我们怎样拿到temp的值?我们应该知道在JS中或者很多语言中,我们每次用一个变量或者函数时,当执行时都会向上找。如下图。var tes...

weixin_39782752的博客 4687

回调函数究竟是什么?

深入研究到底什么是回调函数

ricardoccc的博客 1789
上一篇: MFC消息映射
下一篇: 非模态对话框 建立 销毁
hiside
博客等级 码龄16年 5粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值