zoukankan      html  css  js  c++  java
  • C++中回调函数(CallBack)的使用

    如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过。
    其错误是普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递this指针给其成员函数从而实现成员函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数却-有不同的数据成员。由于this指针的作用,使得将一个CALL-BACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败。要解决这一问题的关键就是不让this指针起作用,通过采用以下两种典型技术可以解决在C++中使用回调函数所遇到的问题。这种方法具有通用性,适合于任何C++。      
      1).   不使用成员函数,为了访问类的成员变量,可以使用友元操作符(friend),在C++中将该函数说明为类的友元即可。      
      2).   使用静态成员函数,静态成员函数不使用this指针作为隐含参数,这样就可以作为回调函数了。静态成员函数具有两大特点:其一,可以在没有类实例的情况下使用;其二,只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数。由于在C++中使用类成员函数作为回调函数的目的就是为了访问所有的成员变量和成员函数,如果做不到这一点将不具有实际意义。解决的办法也很简单,就是使用一个静态类指针作为类成员,通过在类创建时初始化该静态指针,如pThis=this,然后在回调函数中通过该静态指针就可以访问所有成员变量和成员函数了。

    这种处理办法适用于只有一个类实例的情况,因为多个类实例将共享静态类成员和静态成员函数,这就导致静态指针指向最后创建的类实例。为了避免这种情况,可以使用回调函数的一个参数来传递this指针,从而实现数据成员共享。这种方法稍稍麻烦,这里就不再赘述。关于静态方法访问非静态变量和函数的方式请见http://www.cnblogs.com/this-543273659/archive/2011/08/29/2157966.html

    首先明白什么是回调函数:比如说被调函数void callbackf(int n){}要想作为回调函数的话,callbackf必须作为主调函数的形参出现,如void f(void (*p(int)),int n)形式才行!

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    如果我要在static中调用类中的成员,怎么做?
    我得定义一个全局的 指向该类的指针,然后在类的初始化函数里面将 类的this 指针赋值给该全局指针。
    然后在static函数中通过该全局指针调用类的其它成员。

    例子1:

    复制代码
    #include "stdafx.h"
    #include <iostream>
    #include <assert.h>
    using namespace std;
    
    class Test
    {
    public:
    
        friend void callBackFun(void){ cout << "CallBack Function!";} //友元函数作为回调函数 friend方式实现,											  //因为callBackFun默认有一个const Test* 的指针
    };
    
    typedef void (*FPtr)(void);
    
    void Fun(FPtr ptr)
    {
        ptr();
    }
    
    
    
    int main(void)
    {
        Fun(callBackFun); 
    
        return 0;
    }

    例2:

    #include<iostream> using namespace std; class A { public:    static void callback()  //类的成员函数作为回调函数 static方式实现  {   cout<<"回调函数开始执行了!"<<endl;  } }; void f(void (*p)())  {   p();

     } int main() {  void (*p)();  p=A::callback;  f(p);  return 0; }

    还可以把f()函数设为类的成员函数:

    #include<iostream> using namespace std; class A { public:    static void callback()  //类的成员函数作为回调函数 static方式实现  {   cout<<"回调函数开始执行了!"<<endl;  }  void f(void (*p)())  {   p();

     } };

    int main() {  A a;  void (*p)();  p=A::callback;  a.f(p);  return 0; }

    ///什么是回调函数
    回调函数就是那些自己写的,但是不是自己来调,而是给别人来调用的函数。
    消息响应函数就可以看成是回调函数,因为是让系统在合适的时候去调用。这不过消息响应函数就是为了处理消息的,所以就拿出来单做一类了。其实本质上就是回调函数。
    但是回调函数不是只有消息响应函数一种,比如在内核编程中,驱动程序就要提供一些回调函数,当一个设备的数据读写完成后,让系统调用这些回调函数来执行一些后续工作。回调函数赋予程序员这样一种能力,让自己编写的代码能够跳出正常的程序控制流,适应具体的运行环境在正确的时间执行。
    ////////////////////////////////////////////
    普通的函数是:咱们的函数调用系统的函数,
    把你写的程序和系统已经封装好的函数看成两个部分
    你的程序使用系统的函数 那叫 调用
    系统函数使用你的程序函数 就叫回调
    一般多用于系统函数与你的函数要进行异步处理
    比如按键事件,其实是个消息
    你的函数比按键事件更早存在
    所以你要将这个函数做为回调函数提交给系统,
    然后系统在接收到按键事件后,再调用你的函数
    /////////////////////////////////////////////
    比如:void fun(){printf();}
    而回调函数是:系统调用你的函数。
    win32 编程的WndProc,java的事件,c#的delegate都是这种思想。可以说没有坏处,回调使得系统更加灵活。
    函数指针做为函数的参数,传递给一个被调用函数,
    被调用函数就可以通过这个指针调用外部的函数,这就形成了回调
    
    一般的程序中回调函数作用不是非常明显,可以不使用这种形式
    
    最主要的用途就是当函数不处在同一个文件当中,比如动态库,要调用
    其他程序中的函数就只有采用回调的形式
    
    #include "stdio.h"
    #include "conio.h"
    
    int add(int a, int b);
    int libfun(int (*pDis)(int a, int b));
    
    int main(void)
    {
    int (*pfun)(int a, int b);
    
    pfun = add;
    libfun(pfun);//调用int libfun(int (*pDis)(int a, int b))
    
    
    }
    
    int add(int a, int b)
    {
    return a + b;
    
    }
    
    int libfun(int (*pDis)(int a, int b))//回调add()函数
    {
    int a, b;
    a = 1;
    b = 2;
    printf("%d", pDis(a, b));
    
    }
    
    现在这几个函数是在同一个文件当中
    
    假如 
    int libfun(int (*pDis)(int a, int b))
    是一个库中的函数,就只有使用回调了,通过函数指针参数将外部函数地址传入
    来实现调用
    
    函数 add 的代码作了修改,也不必改动库的代码,就可以正常实现调用
    便于程序的维护和升级
    
  • 相关阅读:
    浅析MySQL二进制日志
    MySQL升级
    浅析MySQL复制
    MySQL关于exists的一个bug
    TokuDB存储引擎
    MySQL中RESET SLAVE和RESET MASTER的区别
    MySQL半同步复制
    MySQL线程池
    分析MariaDB初始化脚本mysql_install_db
    Python装饰器
  • 原文地址:https://www.cnblogs.com/zhaoxinshanwei/p/3903374.html
Copyright © 2011-2022 走看看