回调函数的应用误区4(c/s OK版本回调小程序)
VC++深入详解里面说得也挺好:回调函数的实现机制:
1)定义一个回调函数
2)“函数实现者”(回调函数所在的模块)在初始化的时候,将回调函数的函数指针注册给“调用者”。
3)当特定的事件或条件发生的时候,“调用者”使用函数指针调用“回调函数”对事件进行处理。
针对Windows的消息处理机制,窗口过程函数(回调函数)被调用的过程如下:
1)在设计窗口类的时候,将窗口过程函数的地址赋值给lpfnWndProc成员变量
2)调用RegisterClass(&wndclass)注册窗口类,那么系统也就有了上面的窗口过程函数的地址了。
3)当应用程序接收到某一窗口消息时,调用DispatchMessage(&msg)将消息回传给系统。系统则调用窗口过程函数(通过指针)对消息进行处理。
网友一例子对我很有启发,下面先看网页对回调函数的理解:
为什么要用回调呢?比如我要写一个子模块给您用, 来接收远程socket发来的命令.当我接收到命令后, 需要调用您的主模块的函数,
来进行相应的处理.但是我不知道您要用哪个函数来处理这个命令,
我也不知道您的主模块是什么.cpp或.h, 或说, 我根本不用关心您在主模块里怎么处理他, 也不应该关心用什么函数处理他......
怎么办?
使用回调.
我在我的模块A里先定义回调函数类型, 连同回调函数指针.
typedef void (CALLBACK *cbkSendCmdToMain) (AnsiString sCmd);
cbkSendCmdToMain
SendCmdToMain;
这样SendCmdToMain就是个指向拥有一个AnsiString形参, 返回值为void的函数指针.
这样, 在我接收到命令时, 就能够调用这个函数啦. [Page]
...
A_SendCmdToMain(sCommand);
...
但是这样还不够, 我得给一个接口函数(比如Init), 让您在主模块里调用A_Init来注册这个回调函数.
在您的主模块B里, 可能这样声明回调函数,
void CALLBACK YourSendCmdFun(AnsiString
sCmd); //声明
...
void CALLBACK YourSendCmdFun(AnsiString
sCmd); //定义
{
ShowMessage(sCmd);
}
...
调用A_Init函数向我的模块注册回调.可能这样:
A_Init(YourSendCmdFun, ...);
这样, 预期目的就达到了.
需要注意一点, 回调函数一般都要声明为全局的. 假如要在类里使用回调函数, 前面需要加上
static , 其实也相当于全局的.
案例:
A方现在正在做一个项目,项目当中有一个任务交给了B方,让B方代理完成,但是在B执行代理任务期间却需要添加一些只有A方才有的专利信息,
所以这时候A方就要派本方一个专利接口人以协助B方,A方把专利方面的规则交待给这个专利接口人,B方执行代理任务期间遇到合适的机会时就会按专利接口人的要求打上A方定制的专利信息。
解释:
如果A只是单单把任务交给B方代理,自己不搀和进去的话,就很简单了,就如同我们调用printf函数一样,简单调用就可以直接完工。这种情况就是我们最常用的普通函数调用。
而现在的情况是:要在B方的代理任务里面需要添加一些A方专利,所以这里就发生了A调用B,B调用A,相互调用的情形:
A方调用B方的对接接口来完成任务,当B方在对接接口里用到专利函数时,就会回过头来调用A所提供的专利函数,所以就个专利函数就是我们所说的回调函数。
或者说:
回调函数就是一个挂靠函数,各位有意见吗?它就是让你写一个函数,写完后挂靠(Register)到调用者的系统或模块中,当调用者准备使用的时候,就拿过来用(使用函数指针)。
回调函数注定是要被调用者调用的,因为它的出现就是基于被调用者的需求而来的,所以它占的作用比较重要,而普通函数则不然,非必须的。
常见的实现有两种方式:
1,指定专利函数,调用对接接口(带专利函数参数)
A方调用B方的接口函数,由于接口函数本身附带专利函数信息,所以B方可以直接拿来使用,可按要求完成任务。
2,先注册专利函数,再调用对接接口(不带专利函数)
A方调用B方的注册函数,把将A方的专利函数注册到B方。
A方调用B方的接口函数,B方利用已经注册过专利函数,可按要求完成任务。
##################dll.h##################
#pragma once #include "stdio.h" //回调函数类型声明
typedef void (* CALLBACK)(int ); //实现方式一
extern void StartPrint(CALLBACK fun, int interval); //实现方式二
extern void RegisterPrint(CALLBACK fun);
extern void StartPrint(int interval);
##################dll.cpp##################
#include "dll.h" CALLBACK g_pcb = NULL; void StartPrint(CALLBACK fun, int interval)
{
RegisterPrint(fun);
for (int i=0; i<interval; i++) (g_pcb)(i);
} void RegisterPrint(CALLBACK fun)
{
g_pcb = fun;
} void StartPrint(int interval)
{
for (int i=0; i<interval; i++) (g_pcb)(i);
}
##################app.cpp##################
#include <stdio.h>
#include "dll.h" //回调函数定义1:A模块定义,提供给B模块,被B模块调用
void PrintHello(int i)
{
printf("[PrintHello]: Hello-%d\n", i);
} //回调函数定义2
void PrintBye(int i)
{
printf("[PrintBye]: Bye-%d\n", i);
} int main(int argc, char **argv)
{
//注册
RegisterPrint(&PrintHello);
//调用
StartPrint(5); //注册调用合并:好面熟哦,在这里你有没有看到线程创建函数的影子?
StartPrint(&PrintBye, 3);
return 0;
}
运行结果:
[PrintHello]: Hello-0
[PrintHello]: Hello-1
[PrintHello]: Hello-2
[PrintHello]: Hello-3
[PrintHello]: Hello-4
[PrintBye]: Bye-0
[PrintBye]: Bye-1
[PrintBye]: Bye-2
请按任意键继续. . .
回调函数的应用误区4(c/s OK版本回调小程序)的更多相关文章
- 嵌入式&iOS:回调函数(C)与block(OC)回调对比
学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...
- 理解 JavaScript 回调函数并使用
JavaScript中,函数是一等(first-class)对象:也就是说,函数是 Object 类型并且可以像其他一等对象(String,Array,Number等)一样使用.它们可以"保 ...
- 关于js的回调函数的一点看法
算了一下又有好几个月没写博客了,最近在忙公司android的项目,所以也就很少抽时间来写些东西了.刚闲下来,我就翻了翻之前看的东西.做了android之后更加感觉到手机端开发的重要性,现在做nativ ...
- C++ 回调函数的定义与用法
一回调函数 我们经常在C++设计时通过使用回调函数可以使有些应用(如定时器事件回调处理.用回调函数记录某操作进度等)变得非常方便和符合逻辑,那么它的内在机制如何呢,怎么定义呢?它和其它函数(比如钩子函 ...
- javascript 函数初探 (四)--- 回调函数
回调函数 既然函数与任何被赋值给变量的数据是相同的,那么她当然可以像其他数据那样被定义.删除.拷贝,以及当成参数传递给其它函数. 我们定义一个函数,这个函数有两个函数类型的参数,然后他会分别执行这两个 ...
- JS回调函数全解析教程
转自:http://blog.csdn.net/lulei9876/article/details/8494337 自学jQuery的时候,看到一英文词(Callback),顿时背部隐隐冒冷汗.迅速g ...
- 浅谈js回调函数
回调函数原理: 我现在出发,到了通知你”这是一个异步的流程,“我出发”这个过程中(函数执行),“你”可以去做任何事,“到了”(函数执行完毕)“通知你”(回调)进行之后的流程 例子 1.基本方法 ? 1 ...
- nodejs回调函数
阻塞代码实例: main.js var fs=require("fs"); var data=fs.readFileSync("1.txt"); console ...
- 学习js回调函数
<!DOCTYPE HTML> <html> <head> <meta charset="GBK" /> <title> ...
随机推荐
- 如何在winform DataGridView控件的DataGridViewButtonColumn按钮列中禁用按钮
原文:http://msdn.microsoft.com/en-us/library/ms171619(v=vs.85).ASPX public class DataGridViewDisableBu ...
- 【面试题013】在O(1)时间删除链表结点
[面试题013]在O(1)时间删除链表结点 我们要删除结点i,我们可以把结点i的下一个结点j的内容复制到结点i,然后呢把结点i的指针指向结点j的下一个结点.然后在删除结点j. 1.如果结点i位于链表 ...
- 【设计模式六大原则6】开闭原则(Open Close Principle)
定义:一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. 问题由来:在软件的生命周期内,因为变化.升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不 ...
- SDUT图结构练习——最小生成树
http://acm.sdut.edu.cn/sdutoj/showproblem.php?pid=2144&cid=1186 这道题一开始是用prim算法做的,一直错一直错,后来问了帅郭改用 ...
- [转]Ubuntu 12.04 安装屏保
From:http://www.howtogeek.com/114027/how-to-add-screensavers-to-ubuntu-12.04/ How to Add Screensaver ...
- swift学习笔记-UI篇之UIImageView
1.基本使用 将要使用的图片拖入到项目里,我这里使用的是名为“1.jpg”的图片,然后创建UIImageView,并设置要显示的图片为"1.jpg"//1. 基本使用 let im ...
- Java学习笔记之:Java String类
一.引言 字符串广泛应用在Java编程中,在Java中字符串属于对象,Java提供了String类来创建和操作字符串. 创建字符串最简单的方式如下: String str= "Hello w ...
- 用 React 编写2048游戏
1.代码 <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="U ...
- QEvent大全,有中文解释
简述 QEvent 类是所有事件类的基类,事件对象包含事件参数. Qt 的主事件循环(QCoreApplication::exec())从事件队列中获取本地窗口系统事件,将它们转化为 QEvents, ...
- Xaml语法概述及属性介绍
Xaml语法概述 1.命名空间 xmal每个元素都对应着一个类,但是在xmal中,只提供类名是不够的,需要知道该类实在.net的哪个命名空间下面.Xaml解析器才能够正确的解析. 1 < ...