C++——回调函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zy2317878/article/details/80977187

写在前面

这里学习一下回调函数,主要也是参考网上的资料做一个简单的整理。参考文献:

示例代码

#include <iostream>

using namespace std;

typedef void (*Func)(int);
Func p = NULL;

void caller(Func pCallback){
    p = pCallback;
    int result = 1;
    (*p)(result);
}

//void caller(void (*pCallback)(int)){
//    void (*p)(int);
//    p = pCallback;
//    int result = 1;
//    (*p)(result);
//}

void callback(int a){
    cout<< "callback result = " << a << endl;
}

int main(){
    caller(callback);
    getchar();
    return 0;
}

在这一段代码中,首先利用typedef定义了一个函数指针类型,即:

Func p = NULL;
//相当于
void (*p)(int); p = NULL;

所以,如果不使用typedef那么程序只需要将caller函数,替换成注释的那一部分即可。

解释完typedef之后,从main函数开始,函数调用了caller()函数,caller函数需要传递的参数本来就是一个函数指针,指向一个函数(加入名字为function):void function(int a),这样一个函数。在这段程序中,就是定义的callback()函数。观察不难发现callback()函数返回值、传递的参数都与之前说的function函数一模一样。这样就完成了在caller函数中,通过函数指针,完成callback函数的调用。callback就是回调函数,一个被其他函数通过函数指针调用的函数。

总结一下,main()函数中调用caller()函数,并将callback的函数名,即该函数的函数指针传递给caller()函数(函数名就是一个函数指针,也可以新建一个函数指针,让其等于该函数名,如下)。而call()函数接收一个函数指针,并在该函数中通过这个函数指针,完成了对callback()函数的调用。

//int main(){
//    caller(callback);
//    getchar();
//    return 0;
//}

int main(){
    void (*a)(int);
    a = callback;
    caller(a);
    return 0;
}

以上都是函数与函数之间的调用,主要是为了弄明白:

  • 构建接收一个函数指针作为参数的函数;
  • 通过一个函数指针调用指针指向的函数。

与类相关的回调函数

#include <iostream>

using namespace std;

typedef void (*FUNP)();

class Test1{
public:
    Test1(){}
    void func1(FUNP p){
        (*p)();
    }
};

class Test2{
public:
    Test2(){
        Test1 tet1;
        tet1.func1(func2);
    }
    static void func2(){
        cout << "I am Test2" << endl;
    }
};

int main(){
    Test2 tet2;
    return 0;
}

从主函数看,main()函数中实例化了,或者说生成了一个Test2类的对象,tet2,那么就会调用Test2的构造函数,在Test2的构造函数中会实例化一个Test1的对象,并且调用Test1的成员函数,而这个成员函数接收一个函数指针作为函数参数,而这个函数指针指向Test2的成员函数。而Test1的成员函数func1接受一个函数指针,然后调用这个函数指针指向的函数。

这里要注意func2函数要用static描述,这样函数func2就是一个全局函数,可以通过函数名直接访问,如果不加这个修饰符那么就需要通过类名加域访问符::来访问,而要通过这个函数指针使用这样一个一般类成员函数,也需要对类进行实例化。

现在回头来看一下,这里相当于Test2被实例化之后,会触发Test2的实例化函数,而实例化函数又调用了Test1的实例化函数,在Test1实例化函数中接收一个函数指针,通过这个函数指针调用另一个函数,在这个例子中是调用了Test2的类成员函数func2,那么这个函数就是回调函数,通过函数指针调用的函数。

回调函数

回调函数就是通过函数指针调用的函数。一般使用方式是把函数的指针(地址)作为一个参数传递给另一个函数A(将函数指针(地址)作为形参),那么函数A在执行过程中,会再次调用这个指针,从而在函数A中通过函数指针调用了原来的函数。

关键的地方就是要明白,函数的调用可以通过函数指针,而且函数指针也能作为参数被其他的函数接收。而通过函数指针调用的函数就是回调函数。

回调函数的抽象作用

根据之前的例子,我们发现回调函数通过函数指针其实实现的就是,在满足一定条件下、或者说,其他函数、程序段进行了一定程度之后,会调用这个指针,从而实现指针指向的函数的调用。

抽象来看,仅仅站在接收函数指针的函数角度来看,设这个函数叫:caller(),由于函数caller只接受了一个函数指针p_Fnc,它并不知道指针指向的函数内部具体是如何实现的,不知道也不关心更不影响。所以,这样把调用者与被调用者分开了。

回调函数的具体作用

假设要编写一个库,库提供了许多排序算法的实现,但是排序分种类呀,有从小到大、从大到小,对于字符串还有字典顺序、按长度排序、按ASCII大小排序,总之排序方法有很多。但是对于排序算法更关注的是排序的逻辑:“比较”,排序算法关系元素之间的比较结果,如果比较结果满足某种条件,就进行排序。那么这里就可以使用回调函数。

即在排序算法中调用指向比较函数的指针,通过指针调用比较函数,返回比较结果,从而让排序算法继续算法排序。

不止排序的元素的比较方法,实际应用还会考虑到元素的种类,int、char、string等,这时候也可以使用回调函数来通过函数指针调用对应的类型处理的函数。

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

写在最后

把握住,通过函数指针调用一个函数,这个本质就挺好理解的了。

猜你喜欢

转载自blog.csdn.net/zy2317878/article/details/80977187