日志
[MFC][原创]回调函数实现流程具体实现过程
[MFC][原创]回调函数实现流程具体实现过程 第一步:在类成员中或者全局成员声明回调指针typedef void(*CallBackDelegate)(int a);//回调函数,CallBackDelegate可以随便取建议取的名字有意义,a是参数,这里可以添加任意多的参数,更多参数可以用个结构体 第二步:注册回调函数,在类成员函数声明一个注册函数 void RegisterCallBack(CallBackDelegate callback); //这里注意参数必须含有回调函数类型 在类的成员函数声明(可以声明为public或者private) private: CallBackDelegate mycallback; 实现注册函数 void RegisterCallBack(CallBackDelegate callback) { mycallback=callback; } 假设类中有一个函数叫Dowork()则可以写回调函数调用 void Dowork() { //dosomething //例如 for(int i=0;i<1000000;i++) mycallback(i); } 此类结束。 第三步:在另一个类,例如我们常见的对话框类使用回调。 首先在新类中声明函数,注意参数要和上面类回调函数对应。上面类我们暂且称为旧类 public: static void ShowCallBack(int a); 然后在新类实例化旧类。比如旧类叫A,新类叫B 则可以在B初始化中加入 A a; a.RegisterCallBack(ShowCallBack);//ShowCallBack这个是B中声明的,名字任意取 B回调实现: B::ShowCallBack(int a) { //对回调过来的数据int a进行处理 } 这里举2个例子,让大家彻底知道其原理。 第一个就是上面完整例子: 自定义类A的头文件: #pragma once typedef void(*CallBackDelegate)(int a); class A { public: A(); ~A(); void RegisterCallBack(CallBackDelegate callback); //这里注意参数必须含有回调函数类型 void Dowork(); private: CallBackDelegate mycallback; };A.cpp #include "stdafx.h" #include "A.h"
A::A() { }
A::~A() { } void A::RegisterCallBack(CallBackDelegate callback) { mycallback = callback; } void A::Dowork() { //dosomething //例如 for (int i = 0; i<1000000; i++) mycallback(i); }对话框类,这里对话框叫CMFCApplication2Dlg 首先声明 public: CMFCApplication2Dlg(CWnd* pParent = NULL);// 标准构造函数 static void ShowCallBack(int a);在初始化对话框加入以下代码 BOOL CMFCApplication2Dlg::OnInitDialog() { CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE);// 设置大图标 SetIcon(m_hIcon, FALSE);// 设置小图标
// TODO: 在此添加额外的初始化代码 A a; a.RegisterCallBack(ShowCallBack);//ShowCallBack必须静态函数或者全局函数 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE }注意上面还需要引入头文件A,最后对回调函数进行处理 void CMFCApplication2Dlg::ShowCallBack(int a) { //处理数据a }第二个例子,回调函数标准是附带一个LPVOID类型回调上下文,,这是为什么呢?因为LPVOID可以将类对象强制转换为指针,这样便可以访问对象控件或者方法,例如对话框,这个和线程为什么传递一个LPVOID类型是一样的,不然不容易访问对话框控件,大体和上面例子差不多。这个例子不是本人原创,但是基本原理一样,参考博客https://blog.csdn.net/fairytailq/article/details/78376414,这里顺便把代码复制过来看一下: 原理: 参考Button的点击事件。
模块A(Dialog类)需要根据模块B的部分事件(Button点击事件)来实现逻辑,只需要在模块A中实现模块B的回调函数即可(Dialog类实现Button的点击事件)。模块B负责实现回调函数的原型,回调函数的注册。
(注:还可参考C#的事件进行理解,回调函数的原型即C#中的委托Delegate,回调函数的注册可以看成事件Event的注册,A中的回调函数,Event+=Fun中的Fun。)
使用情况: 如:通信时,异步接收。
例程部分代码说明: 摘取主要内容,此处我没有在B类中写线程来异步触发回调函数,所以开放接口caller()来外部触发,模拟实际情况。 注:例程是我缩减整理的,可能有部分错误,了解其精髓即可。
//B.h文件
//回调函数参数结构体 struct tagCallbackParam { public: bool m_bParam0; int m_nParam1; CString m_szParam2; tagCallbackParam() { m_bParam0 = false; m_nParam1 = 100; m_szParam2 = _T("100"); } };
class CB { public: CB(); virtual ~CB(); typedef void(*CallbackPrototype)(LPVOID lp, const tagCallbackParam& rParam); void Register(CallbackPrototype pCallBackfun, LPVOID lp); void Caller(); private: CallbackPrototype m_pFun; LPVOID m_lp; tagCallbackParam m_callbackParam; };
//B.cpp文件 //............ void CB::Register(CallbackPrototype myCallBackfun, LPVOID lp) { //注册回调函数(/类似注册事件) m_pFun = myCallBackfun; m_lp = lp; }
void CB::Caller() { //实现一些操作信息 //.............. m_callbackParam.m_nParam1++; m_callbackParam.m_szParam2.Format(_T("参数2=%d"),m_callbackParam.m_nParam1); //触发回调函数(/类似触发事件) m_pFun(m_lp, m_callbackParam); }
//A.h文 //-此处我使用的是窗口类(Dialog.h) #include "B.h" //......... class CB { public: //............窗口类的一堆声明。 CB m_cb; //回调函数,参数列表,返回值必须和回调函数原型一样,必须是静态,方便在初始化InitDialog时注册。 static void CallBackDefine(LPVOID lp, const tagCallbackParam& rParam); }
//A.cpp //-此处我添加了两个EditControl(显示参数信息),一个Button(负责触发回调函数)
BOOL A::OnInitDialog() { //......窗口初始化一大推 m_cb=B(); m_cb.Register(CallBackDefine,this); }
//回调函数具体实现 void A::CallBackDefine(LPVOID lp, const tagCallbackParam& rParam) { A* pDlg = (A*)lp;A CString szParam1Value; szParam1Value.Format(_T("参数1=%d"), rParam.m_nParam1); pDlg->GetDlgItem(IDC_EDIT1)->SetWindowTextW(szParam1Value); pDlg->GetDlgItem(IDC_EDIT2)->SetWindowTextW(rParam.m_szParam2); }
void A::OnBnClickedButton1() { m_cb.CallbackUser(); } |