• 回调函数的应用误区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   , 其实也相当于全局的.
     
     换种说法:
        “调用者”相当于win7系统;”回调函数“相当于软件的窗口响应函数,它会对鼠标的不同操作做出相应的响应。比如拖动计算器标题栏会移动窗口,双击计算器标题栏会无响应;拖动画板标题栏会移动窗口,双击画板标题栏时会全屏显示窗口。
        ”调用者“自己不会做”回调函数“的具体实现,它管不了日益增多的用户自定义软件,它只负责调用接口,具体怎么做要看软件们自己的思想了。
        “调用者”会在某一时刻用到“回调函数”,因为单击双击这些操作是鼠标在win7系统最常见的功能。
        “调用者”与“回调函数”的对接点是:”回调函数“指针,之所以使用函数指针,还是上面所说的优点,使用它可以对所有软件的某些消息做统一管理,屏幕掉同操作不同响应这些实现细节。
        对接点的入口我们称之为:”回调函数“注册函数,win7系统通过这个注册函数可以拿到回调函数指针,利用它就可以进行相应的响应操作。
    下面是我的理解,希望你也能理解:

    案例:

    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
    ", i);
    }
    
    //回调函数定义2
    void PrintBye(int i)
    {
    	printf("[PrintBye]: Bye-%d
    ", 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
    请按任意键继续. . .

  • 相关阅读:
    JS 数字时钟的代码(摘录,忘了是从哪了)
    数据写入DataTable C# 2005
    C# 进制转化问题测试下再说(网上的直接转化不好用)
    防sql 注入,就是将sql 的执行命令给排除
    今天研究了一下午网站窄屏/宽屏的切换实现
    解决VS2005下中文输入法全角半角混乱的补丁
    一些实用的站长查询工具
    UE(用户体验)无处不在,留心处处皆学问
    添加了方便聚合的链接
    该好好整理一下自己了
  • 原文地址:https://www.cnblogs.com/jacklikedogs/p/3748085.html
Copyright © 2020-2023  润新知