zoukankan      html  css  js  c++  java
  • c++回调函数

    对于很多初学者来说,往往觉得回调函数很神秘,很想知道回调函数的工作原理。本文将要解释什么是回调函数、它们有什么好处、为什么要使用它们等等问题,在开始之前,假设你已经熟知了函数指针。

      什么是回调函数?

      简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。

      为什么要使用回调函数?

      因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。

       如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell排 序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、 float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。

      回调可用于通知机制,例如,有时要在程序中设置一个 计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调, 来通知我们的程序事件已经发生。实际上,SetTimer() API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。

      另一个使用回调机制的 API函数是EnumWindow(),它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程序。如果被调用者返回一个 值,就继续进行迭代,否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为 基于返回值,它将继续执行或退出。

      不管怎么说,回调函数是继续自C语言的,因而,在C++中,应只在与C代码建立接口,或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C++中应使用虚拟方法或函数符(functor),而不是回调函数。

    下面是自己写的一个简单的回调函数,相比其他的那些复杂的代码,这个更容易理解:

    #include<stdio.h>
    #include<stdlib.h>
    void perfect(int n)
    {
     int i=1;
        int count=0;
     for(i=1;i<n;i++)
     {
        
      if(0==n%i)
      {
       count+=i;
      }
     }
     if(count==n)
      printf("%d是完数 ",n);
     else printf("%d不是完数 ",n);
    }
    void myCallback(void (*perfect)(int ),int n)
    {
     perfect(n);
    }

    int main()
    {
     int n;
     printf("请输入一个正整数 ");
     scanf("%d",&n);

     myCallback(perfect,n);
     return 0;
     
    }

    C/C++之回调函数

         今天讨论下C/C++中的回调函数。

         在理解“回调函数”之前,首先讨论下函数指针的概念。

    函数指针

    (1)概念:指针是一个变量,是用来指向内存地址的。一个程序运行时,所有和运行 相关的物件都是需要加载到内存中,这就决定了程序运行时的任何物件都可以用指针来指向它。函数是存放在内存代码区域内的,它们同样有地址,因此同样可以用 指针来存取函数,把这种指向函数入口地址的指针称为函数指针。

    (2)先来看一个Hello World程序:

    int main(int argc,char* argv[])
    {
    printf("Hello World! ");
    return 0;
    }

           然后,采用函数调用的形式来实现:

    复制代码
    void Invoke(char* s);

    int main(int argc,char* argv[])
    {
    Invoke("Hello World! ");
    return 0;
    }

    void Invoke(char* s)
    {
    printf(s);
    }
    复制代码

          用函数指针的方式来实现:

    复制代码
    void Invoke(char* s);

    int main()
    {
    void (*fp)(char* s); //声明一个函数指针(fp)
    fp=Invoke; //将Invoke函数的入口地址赋值给fp
    fp("Hello World! "); //函数指针fp实现函数调用
    return 0;
    }

    void Invoke(char* s)
    {
    printf(s);
    }
    复制代码

          由上知道:函数指针函数的声明之间唯一区别就是,用指针名(*fp)代替了函数名Invoke,这样这声明了一个函数指针,然后进行赋值fp=Invoke就可以进行函数指针的调用了。声明函数指针时,只要函数返回值类型、参数个数、参数类型等保持一致,就可以声明一个函数指针了。注意,函数指针必须用括号括起来 void (*fp)(char* s)。

         实际中,为了方便,通常用宏定义的方式来声明函数指针,实现程序如下:

    复制代码
    typedef void (*FP)(char* s);
    void Invoke(char* s);

    int main(int argc,char* argv[])
    {
    FP fp; //通常是用宏FP来声明一个函数指针fp
    fp=Invoke;
    fp("Hello World! ");
    return 0;
    }

    void Invoke(char* s)
    {
    printf(s);
    }
    复制代码

    函数指针数组

          下面用程序对函数指针数组来个大致了解:

    复制代码
    #include <iostream>
    #include <string>
    using namespace std;

    typedef void (*FP)(char* s);
    void f1(char* s){cout<<s;}
    void f2(char* s){cout<<s;}
    void f3(char* s){cout<<s;}

    int main(int argc,char* argv[])
    {
    void* a[]={f1,f2,f3}; //定义了指针数组,这里a是一个普通指针
    a[0]("Hello World! "); //编译错误,指针数组不能用下标的方式来调用函数

    FP f[]={f1,f2,f3}; //定义一个函数指针的数组,这里的f是一个函数指针
    f[0]("Hello World! "); //正确,函数指针的数组进行下标操作可以进行函数的间接调用

    return 0;
    }
    复制代码

    回调函数

    (1)概念:回调函数,顾名思义,就是使用者自己定义一个函数,使用者自己实现这个函数的程序内容,然后把这个函数作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。

    (2)标准Hello World程序:

    int main(int argc,char* argv[])
    {
    printf("Hello World! ");
    return 0;
    }

          将它修改成函数回调样式:

    复制代码
    //定义回调函数
    void PrintfText()
    {
    printf("Hello World! ");
    }

    //定义实现回调函数的"调用函数"
    void CallPrintfText(void (*callfuct)())
    {
    callfuct();
    }

    //在main函数中实现函数回调
    int main(int argc,char* argv[])
    {
    CallPrintfText(PrintfText);
    return 0;
    }
    复制代码

          修改成带参的回调样式:

    复制代码
    //定义带参回调函数
    void PrintfText(char* s)
    {
    printf(s);
    }

    //定义实现带参回调函数的"调用函数"
    void CallPrintfText(void (*callfuct)(char*),char* s)
    {
    callfuct(s);
    }

    //在main函数中实现带参的函数回调
    int main(int argc,char* argv[])
    {
    CallPrintfText(PrintfText,"Hello World! ");
    return 0;
    }
    复制代码

    c/c++比较灵活的方法:回调函数和函数指针

    当代码量比较小或者需求固定的时候,可以在一个函数里绑定另一个函数,实现函数互调。但当需要经常改变函数或需要实现动态调用时,绑定的参量就不能实现。这时候需要用到函数指针和函数回调

    回调函数:回调函数是一个不显式调用的函数,通过将回调函数的地址传给调用者从而实现调用

    函数指针:指向函数的指针,可以把函数指针传入另一个函数作为形参,实现回调,首先声明指针

    void f();//这是一个函数原型,无输入,输出void型

    void (*)()//左边圆括弧中的星号是函数指针声明的关键,另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数,注意还没有创建函数指针

    unsigned psize = sizeof (void (*) ()); // 获得函数指针的大小

    void (*p) (); //声明指针,p是指向函数的指针,该函数无输入,返回值的类型为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋值, 

    void func()
    {
            //do something
    }
    p = func; //p的赋值可以不同,但一定要是函数的地址,并且署名和返回类型相同。

    传递回调函数的地址给调用者:现在可以将p传递给另一个函数(调用者) caller(),它将调用p指向的函数,而此函数名是未知的:
    void caller(void (*fnp) ())
    {
            fnp();
    }
    void func();
    int main()
    {
          p = func; 
          caller(p); //传递函数地址到调用者

    如果赋了不同的值给p(不同函数地址),那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。
    值的内容是署名匹配的函数名和返回类型。例如:创建指针变量,只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:

    #include <iostream>

    int main()
    {
        void caller(void (*) ()); //函数声明
        void func(); //函数声明
        void (*p) (); //定义指针变量
        p=func; //指针变量赋值
        caller(p); //回调
        getchar();
    }  

    //回调函数
    void caller(void (*fnp) ())
    {
        printf("调用成功");
        fnp();
    }

    //被调函数
    void func()
    {
        printf("回调成功");
    }

    这是比较简单的情况,大部分情况被调函数都有形参和返回值,回调函数也有返回值,但分析方法是相同的。

    from 清水河畔

     
     

    C++ 成员函数 回调函数的实现

    [日期:2014-06-10] 来源:Linux社区  作者:Linux [字体: ]
     

    1.回调函数的说明:

    在进行软件开发的过程中,常会用到一些声明为CALLBACK的函数,这些函数就是回调函数。使用回调函数可以改善软件的结构、提高软件的复用性。 比如,在一个规模较大的软件项目中,可以将一些资或相对独立的处理模块封装到动态连接库(DLL) 中,然后通过回调函数在不同的场合来使用这些资源和模块。利用回调函数还可以进行程序间复杂的通信,实现一些通知的功能,在某些场合它是比消息更合适的一 种方式;在一些特殊的情况下,回调函数更有不可替代的作用。Win32 API 中有许多回调函数的应用,在进行软件设计时也会经常用到这种函数,而有些时候则需要编写自己的回调函数。因此,理解回调函数的原理并掌握它的基本用法是非 常必要的。

    C ++ 是当代使用最广泛的语言,从嵌入式系统到大型机系统、从Linux到Windows,在大型系统的编制中,到处都是它的身影。它以高效和易编程性获得了许 多资深程序员的信赖。在DirectX Play 开发过程中,经常需要使用到回调函数,直接使用回调函数显得复杂麻烦,采用用C + + 实现对回调函数的封装, 使回调函数变得方便实用,对于DirectX Play 等编程就显得是非常有意义的。
    回调函数简单讲就是一个函数指针。写一个函数,然后把函数地址传递给这个函数指针就可以了。

    回调函数的原形对C ++ 的成员函数用做回调函数的影响是什么?
    编写回调函数简单地说就是函数原形一致。函数的输入参数,输出参数一致 是很容易保证的。要注意调用类型一致性。函数传参数有好几种类型,搞错了传参数的方式,系统必然运行错误。一般来说都是WINAPI 传参数方式。要注意C ++ 的类的成员函数和一般的C 函数的区别。C + + 类采用this 规则传递函数。在使用类的成员函数作为回调函数,要求该成员函数被声名为静态成员函数,并且注意函数声名的时候要同时声明好参数传递规则。
     
    2.我的回调函数的理解,
     
    模块A ,模块B,如果模块B 中调用模块A 的东西, 在模块A中发生一个事件或操作,调用B的函数处理,这就才用了回调函数的机制。

    C++ Primer Plus 第6版 中文版 清晰有书签PDF+源代码 http://www.linuxidc.com/Linux/2014-05/101227.htm

    读C++ Primer 之构造函数陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm

    读C++ Primer 之智能指针 http://www.linuxidc.com/Linux/2011-08/40177.htm

    读C++ Primer 之句柄类 http://www.linuxidc.com/Linux/2011-08/40175.htm

    C++11 获取系统时间库函数 time since epoch http://www.linuxidc.com/Linux/2014-03/97446.htm

    C++11中正则表达式测试 http://www.linuxidc.com/Linux/2012-08/69086.htm

    3.例子的实现

    模块A的代码
    #ifndef __A_H
    #define __A_H

    class A
    {
    public:
    A(void){}
    public:
    ~A(void){}


    typedef void (*perfect)(int );  //声明回调函数


    public:
    void CallBackFunc(void (*perfect)(int ),int n)  //给模块B调用的函数
    {
      perfect(n);  //调用的函数
    }


    };
    #endif  //__A_H

    模块B的代码

    #include <iostream>
    #include "A.h"
    using namespace  std;

    void perfect(int n)  //这个函数要求是全局的,或者是类中的静态成员变量
    {
    cout<<n<<endl;
    }


    int main()
    {
    A a;
    a.CallBackFunc(perfect,100);    //调用模块A的代码。


    return 0;


    }

    静态成员函数调用非静态成员函数  应用于回调函数

    代码如下
    模块A的代码
    #ifndef __CALLBACKTEST_H
    #define __CALLBACKTEST_H


    class CallBackTest
    {
    public:
    CallBackTest(void);
    public:
    ~CallBackTest(void);
    typedef void (*perfect)(void*,int );  //声明回调函数
    public:
      void CallBackFunc(void* pThisOBject,void (*perfect)(void*,int ),int n)
    {
    p=perfect;
    m_n=n;
    m_pThisObject=pThisOBject;
    }


      void ExecBackFunc()
      {
    p(m_pThisObject,m_n);
      }


    private: 
    perfect  p;
    int m_n;
    void* m_pThisObject;
    };


    #endif //__CALLBACKTEST_H

    模块B的代码

    #include <iostream>
    #include "CallBackTest.h"
    using namespace std;


    class testMai
    {
    public:
    static void perfect(void *pdata,int n)
    {
    testMai* pObject=(testMai*)pdata;
    pObject->test(n);


    }


    void Exec()
    {
    CallBackTest callbackTest;
    callbackTest.CallBackFunc(&this,perfect,100);


    cout<<"ni hao"<<endl;


    callbackTest.ExecBackFunc();


    }


    void test(int n)
    {
    int i=1;
    int count=0;
    m=9;
    for(i=1;i<n;i++)
    {


    if(0==n%i)
    {
    count+=i;
    }
    }
    if(count==n)
    //printf("%d是完数 ",n);
    {


    cout<<n<<"是完数"<<endl;
    }
    else 
    {
    cout<<n<<"bu shi de "<<endl;
    }
    }


    private:
    int m;


    };


    int main()
    {
    testMai testMai;
    testMai.Exec();
    return 0;
    }

  • 相关阅读:
    PHP中的$_POST变量
    leetcode problem 32 -- Longest Valid Parentheses
    leetcode problem 31 -- Next Permutation
    leetcode problem 11 Container With Most Water
    leetcode problem 10 Regular Expression Matching(动态规划)
    leetcode problem 6 ZigZag Conversion
    leetcode problem (5) Longest Palindromic Substring
    leetcode problem (2-4)
    extern “C”的作用
    C++11中新特性之:unordered_map
  • 原文地址:https://www.cnblogs.com/timssd/p/4781610.html
Copyright © 2011-2022 走看看