回调函数 订阅
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。回调方法 是 任何一个 被 以该回调方法为其第一个参数 的 其它方法 调用 的方法。很多时候,回调是一个当某些事件发生时被调用的方法。 展开全文
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。回调方法 是 任何一个 被 以该回调方法为其第一个参数 的 其它方法 调用 的方法。很多时候,回调是一个当某些事件发生时被调用的方法。
信息
意    思
通过函数指针调用的函数
作    用
对特定的事件或条件进行响应
中文名
回调函数
外文名
Callback Functions
回调函数机制
⑴定义一个回调函数;⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
收起全文
精华内容
下载资源
问答
  • 回调函数

    千次阅读 多人点赞 2019-09-09 17:36:28
    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数回调函数不是由该函数的实现方直接调用,而是在特定...

    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

    其实回调就是一种利用函数指针进行函数调用的过程.

    为什么要用回调呢?比如我要写一个子模块给你用,来接收远程socket发来的命令.当我接收到命令后,需要调用你的主模块的函数, 来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令,我也不知道你的主模块是什么.cpp或者.h,或者说,我根本不用关心你在主模块里怎么处理它,也不应该关心用什么函数处理它......怎么办呢?使用回调!

    —— lone wolf

    使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。

    —— 某专家

    回调函数,就是由你自己写的。你需要调用另外一个函数,而这个函数的其中一个参数,就是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可以在回调函数里完成你要做的事。

    —— 绿叶

    回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。

    —— jufengfeng

    看了这么多的资料,我只将每位的定义总结一下就一句话:使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己写的一个函数(这个函数就是回调函数)的地址作为参数传递给那个函数。回调其实就是提供使用某模块的一种方法。回调函数就好比是一个中断处理函数。

     

    3. 回调函数的作用?应该在什么情况下使用?

    用过STL的人都知道,在STL中众多算法和程序都用到回调函数,这实现了一种策略。只要任何符合我的标准的函数和计算都可以用我这个公式。你可以实现各种各样的回调函数,只要符合我的格式就能用。就上面的程序来说,你只要函数格式符合cllback第二个参数的格式不论你给别人做饭、铺床叠被都可以正常工作。这就是回调的作用,把回调实现留给别人。这是一个用法。
    有一位朋友用分层的概念来解释了回调机制:callback函数为B层,main函数和print函数为A层,A层调用了B层的回调函数callmeback,而B层的回调函数调用了A层的实现函数print。说白了B层就是一个接口。

    有一位朋友用分层的概念来解释了回调机制:callback函数为B层,main函数和print函数为A层,A层调用了B层的回调函数callmeback,而B层的回调函数调用了A层的实现函数print。说白了B层就是一个接口。
    最后要注意的是:回调函数要么是全局函数,要么是静态函数!

    
    #include <stdio.h>
    //回调函数
    int ADD(int (*callback)(int,int), int a, int b){
    	return (*callback)(a,b);//此处回调add函数...
    }
    //普通函数
    int add(int a, int b){
    	return a + b;
    }
     
    int main(void){
    	printf("%d\n",add(1,2));
    	printf("%d\n",ADD(add,1,2));
    	return 0;
    }

     

    C回调函数

    C++回调函数扩展自C回调函数,要想理解C++回调函数,先要理解C回调函数。我们通过一个实例来讲解C回调函数的使用方法。

    //callbackTest.c
    //1.定义函数onHeight(回调函数)
    //@onHeight 函数名
    //@height   参数
    //@contex   上下文
    void onHeight(double height, void* contex)
    {
    	sprint("current height is %lf",height);
    }
    
    //2.定义onHeight函数的原型
    //@CallbackFun 指向函数的指针类型
    //@height      回调参数,当有多个参数时,可以定义一个结构体
    //@contex      回调上下文,在C中一般传入nullptr,在C++中可传入对象指针
    typedef void (*CallbackFun)(double height, void* contex);
    
    //3.定义注册回调函数
    //@registHeightCallback 注册函数名
    //@callback             回调函数原型
    //@contex               回调上下文
    void registHeightCallback(CallbackFun callback, void* contex)
    {
    	double h=100;
    	callback(h,nullptr);
    }
    
    //4.main函数
    void main()
    {
    	//注册onHeight函数,即通过registHeightCallback的参数将onHeight函数指针
    	//传入给registHeightCallback函数,在registHeightCallback函数中调用
    	//callback就相当于调用onHeight函数。
    	registHeightCallback(onHeight,nullptr);
    }

    程序的运行结果是:
    current height is 100
    很多时候,注册的时候并不调用回调函数,而是在其他函数中调用,那我们可以定义一个CallbackFun全局指针变量,在注册的时候将函数指针赋给它,在要调用的调用它。如

    //定义全局指针变量
    CallbackFun* m_pCallback;
    //定义注册回调函数
    void registHeightCallback(CallbackFun callback, void* contex)
    {
    	m_pCallback = callback;
    }
    //定义调用函数
    void printHeightFun(double height)
    {
    	m_pCallback(height,nullptr);
    }
    //main函数
    void main()
    {
    	//注册回调函数onHeight
    	registHeightCallback(onHeight,nullptr);
    	//打印height
    	double h=99;
    	printHeightFun(99);
    }

     程序的运行结果是:
    current height is 99

    C++回调函数

    C++回调函数扩展自C,与C略有不同的是,C++可以使用全局函数和静态函数作为回调函数。考虑到全局函数会破坏封装性,所以一般都用静态成员函数。故除了理解函数指针,还要理解静态成员函数,具体一点是在静态成员函数中访问非静态成员函数的方法,因为我们很可能需要获取静态成员函数中的数据。

    使用场景描述

    比如说你使用了别人提供的sdk,这个sdk可能来自供应商,也有可能来自你的同事,他就提供给你一个注册回调函接口,比如就下面这个,你可以通过回调函数获取到height(某种传感器的实时返回的数据),你要怎么做?

    C++回调函数定义

    //CallbackFun类型
    //@CallbackFun 指向函数的指针类型
    //@height      回调参数,当有多个参数时,可以定义一个结构体
    //@contex      回调上下文,在C中一般传入nullptr,在C++中可传入对象指针
    typedef void (*CallbackFun)(double height, void* contex);
    
    //注册回调函数接口
    //@registHeightCallback 注册函数名
    //@callback             回调函数原型
    //@contex               回调上下文
    void registHeightCallback(CallbackFun callback, void* contex)

    首先,你要定义一个静态成员函数并注册。

    //sensorTest.cpp
    //接收数据类class Sensor
    class Sensor{
    public:
    	Sensor(){}
    	~Sensor(){}
    	//定义回调函数onHeight
    	static void onHeight(double height, void* contex)
    	{
    		cout << "current height is  " << height << endl;
    	}
    	//定义注册回调函数
    	void registCallback()
    	{
    		registHeightCallback(onHeight, this);
    	}
    }
    
    //main 函数
    void main()
    {
    	Sensor sens;
    	sens.registCallback();
    }

    运行程序,我们发现控制台一直在打印
    current height is **
    说明我们的回调函数正确实现了。到这一步不难,只要掌握基本的回调函数概念都能实现。
    现在我们有这样一种情况,我们有另外一个类,要在这个类里面实时打印获取的数据,要怎么做呢?

    静态成员函数访问非静态成员函数的方法

    我们知道静态成员函数中是只能出现静态变量和静态函数的,但是有些时候真的需要访问非静态成员函数或变量,比如我上面说的那种情况。让我们先来实现对同一个类中的非静态成员函数的访问。
    修改class Sensor如下

    //接收数据类class Sensor
    class Sensor{
    public:
    	Sensor(){}
    	~Sensor(){}
    	//定义回调函数onHeight
    	static void onHeight(double height, void* contex)
    	{
    		//cout << "current height is  " << height << endl;
    		Sensor* sen = (Sensor*)contex;
    		if(sen)  //注意判断sen是否有效
    			sen->getHeight(height);
    	}
    	//定义注册回调函数
    	void registCallback()
    	{
    		registHeightCallback(onHeight, this);
    	}
    	//新增的成员函数
    	void getHeight(double height)
    	{
    		cout << "current height is  " << height << endl;
    	}
    }

    如此修改之后,得到与修改前一样的效果(实时打印height),关键点在于注册回调函数的时候将Sensor对象的指针传给了contex,在回调函数中又将contex转换为Sensor对象指针,所以能调用普通函数。
    同理,如果注册时传入某一个对象的指针,就可以在回调函数中对该对象进行操作,这就是我们可以在一个对象中回调另一个对象的思想。

    回调对象

    现在开始解决之前提出的问题,本质是不变的,回调是指针传递,可以是函数指针,也可以是对象指针。

    //先定义一个类class DataPrint
    //打印数据类class DataPrint
    class DataPrint{
    public:
    	DataPrint(){}
    	~DataPrint(){}
    	void printHeight(double height)
    	{
    		cout << "print height is " << height << endl;
    	}
    }
    
    //要在类Sensor中增加DataPrint的指针和一个DataPrint指针赋值函数,class Sensor修改为
    //接收数据类class Sensor
    class Sensor{
    public:
    	Sensor(){}
    	~Sensor(){}
    	//定义回调函数onHeight
    	static void onHeight(double height, void* contex)
    	{
    		DataPrint* dp = (DataPrint*)contex;
    		if(dp)  //注意判断dp是否有效
    			dp->printHeight(height);
    	}
    	//定义注册回调函数
    	void registCallback()
    	{
    		registHeightCallback(onHeight, m_pDataPrint );
    	}
    	//新增的成员函数
    	void getHeight(double height)
    	{
    		//cout << "current height is  " << height << endl;
    	}
    	void setDataPrint(DataPrint* dp)
    	{
    		m_pDataPrint = dp;
    	}
    private:
    	DataPrint* m_pDataPrint;
    }
    
    //main主函数
    void main()
    {
    	DataPrint* dp=new DataPrint();
    	Sensor* sens=new Sensor();
    	//注意这两句的顺序不能颠倒
    	sens->setDataPrint(dp);
    	sens->registCallback();
    }

     这样就能实现在另一个类中取得回调函数的数据,如果无法保证DataPrint的实例化一定在Sensor之前,我们可以这样做

    //先定义一个类class DataPrint
    //打印数据类class DataPrint
    class DataPrint{
    public:
    	DataPrint(){}
    	~DataPrint(){}
    	void printHeight(double height)
    	{
    		cout << "print height is " << height << endl;
    	}
    }
    
    //要在类Sensor中增加DataPrint的指针和一个DataPrint指针赋值函数,class Sensor修改为
    //接收数据类class Sensor
    class Sensor{
    public:
    	Sensor(){}
    	~Sensor(){}
    	//定义回调函数onHeight
    	static void onHeight(double height, void* contex)
    	{
    		Sensor* sen= (Sensor*)contex;
    		if(sen)  //注意判断sen是否有效
    			sen->getHeight(height);
    	}
    	//定义注册回调函数
    	void registCallback()
    	{
    		registHeightCallback(onHeight, m_pDataPrint );
    	}
    	//新增的成员函数
    	void getHeight(double height)
    	{
    		if(m_pDataPrint )
    			m_pDataPrint ->printHeight(height);
    	}
    	void setDataPrint(DataPrint* dp)
    	{
    		m_pDataPrint = dp;
    	}
    private:
    	DataPrint* m_pDataPrint;
    }
    
    //main主函数
    void main()
    {
    	DataPrint* dp=new DataPrint();
    	Sensor* sens=new Sensor();
    	//注意这两句的顺序可以颠倒
    	sens->setDataPrint(dp);
    	sens->registCallback();
    }

    两个的区别是一个直接注册指定类的对象指针,另一个注册当前类的对象指针,间接调用另一个类的对象指针。

    更复杂的讨论

    刚才讨论的问题稍微复杂一点了,不过应该也容易理解,但是我们在实际项目中遇到的情况可能比这个复杂。比如在有层次的软件工程中,回调函数在底层,显示数据的类在上层,我们要如何把底层的数据显示到上层去?容易想到的是上层调用底层,如开个timer刷新,但这是不对的,你无法做到实时调用,你需要的是一个异步的机制,即底层一发生上层就能接收到。
    怎么做呢?还是一样的道理,把上层的类的对象指针传给底层,这时底层需要包含上层的头文件,但这是不对的,上层已经包含了底层的头文件,它们不能互相包含,如何解决这个问题?那就要用到C++继承的特性,首先在底层定义一个基类,然后在上层继承它,在上层实例化这个继承类后,将其指针设置给底层,底层对该指针的操作就是对继承类对象的操作,以此实现数据的传递。

    参考:

    [1] https://www.jianshu.com/p/26784d962f58

    [2] https://blog.csdn.net/weixin_40237626/article/details/82801409

    [3] https://blog.csdn.net/sinat_38183777/article/details/83958887

    [4] https://blog.csdn.net/yidu_fanchen/article/details/80513359

    展开全文
  • 深入理解:回调函数

    万次阅读 多人点赞 2019-06-21 15:36:09
    关于回调函数到底是什么,已经困扰了我很久了~ 在知乎上看到几位大神的帖子,才恍然大悟 作者:no.body 链接:https://www.zhihu.com/question/19801131/answer/27459821 来源:知乎 作者:常溪玲 链接...

    关于回调函数到底是什么,已经困扰了我很久了~

    在知乎上看到几位大神的帖子,才恍然大悟

    作者:no.body
    链接:https://www.zhihu.com/question/19801131/answer/27459821
    来源:知乎

    作者:常溪玲
    链接:https://www.zhihu.com/question/19801131/answer/13005983
    来源:知乎

    首先要明确的一点是,函数也可以作为函数的参数来传递

    好了,有了这个概念我们来说明回调函数到底是怎么回事

    首先至少要有 3 种类型的函数

    • 主函数:相当于整个程序的引擎,调度各个函数按序执行

    • 回调函数:一个独立的功能函数,如写文件函数

    • 中间函数:一个介于主函数和回调函数之间的函数,登记回调函数,通知主函数,起到一个桥梁的作用

    接下来我们一起来看下示例代码:

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    # 回调函数1
    def callback1(x):
        return x * 2 
    
    # 回调函数2
    def callback2(x):
        return x ** 2
    
    # 中间函数
    def middle(x, func):
        return 100 + func(x)
    
    # 主函数
    def main():
    
        x = 1 
    
        a = middle(x, callback1)
        print(a)
    
        b = middle(x, callback2)
        print(b)
    
        c = middle(x, lambda x: x + 2)
        print(c)
    
    main()
    

    运行结果:

    102
    101
    103
    

    代码看懂以后我们接下来分析一下代码的逻辑

    首先我们在主函数执行过程中需要用到一个功能 x * 2,而 callback1 函数就提供这个功能,我们就把这个函数称之为 回调函数(至于为什么要叫“回调函数”,不能叫别的呢?其实这只是人为规定的一个名字。你也可以叫“极客点儿专属函数”,但是到时候你又会问为什么要叫“极客点儿专属函数”,它特么的总的有个名字吧!所以叫“回调函数”就是王八的屁股:规定!)。

    这时候我们的 主函数 要调用它,但是有的时候在开发过程中遇到需要写硬盘的操作,这时候我们为了避免程序的阻塞,就需要用到异步 I/O。就是你自己先写着玩儿,爸爸去干别的事情去了,等你完事儿再来通知我。正是因为这种机制所以得有一个 登记回调函数通知主函数执行完成 的“地方”,这个地方就是 中间函数

    有上述内容我们就可以推导出回调函数执行的流程了:

    1. 主函数需要调用回调函数

    2. 中间函数登记回调函数

    3. 触发回调函数事件

    4. 调用回调函数

    5. 响应回调事件

    回调实际上有两种:阻塞式回调延迟式回调 也可以叫做 同步回调异步回调

    两者的区别在于:

    在阻塞式回调里,回调函数的调用一定发生在主函数返回之前

    在延迟式回调里,回调函数的调用有可能是在起始函数返回之后

    上述示例均为 同步回调,异步需要用到多进程、多线程、协程这些概念,下次有机会再说

    最后用一个例子说明一下到底说明是回调函数:

    你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。

    在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做 触发回调事件,店员给你打电话叫做 调用回调函数,你到店里去取货叫做 响应回调事件

    展开全文
  • JS中回调函数(callback)理解

    万次阅读 多人点赞 2018-07-24 17:46:16
    今天有个刚入行的小兄弟问到了回调函数,讲解了一番以后觉得不能白讲,得把这些东西记下来,虽然很基础。。。 介绍 首先从英文介绍开始 A callback is a function that is passed as an argument to another ...

    前言

    今天有个小兄弟问到了回调函数,讲解了一番以后觉得不能白讲,得把这些东西记下来,虽然很基础。。。

    介绍

    首先从英文介绍开始

    A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.

    这里只是js里的解释。

    开始

    字面上理解下来就是,回调就是一个函数的调用过程。那么就从理解这个调用过程开始吧。函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b。那么这个过程就叫回调。

    其实中文也很好理解:回调,回调,就是回头调用的意思。函数a的事先干完,回头再调用函数b。

    举个现实的例子:约会结束后你送你女朋友回家,离别时,你肯定会说:“到家了给我发条信息,我很担心你。”对不,然后你女朋友回家以后还真给你发了条信息。小伙子,你有戏了。

    其实这就是一个回调的过程。你留了个函数b(要求女朋友给你发条信息)给你女朋友,然后你女朋友回家,回家的动作是函数a。她必须先回到家以后,函数a的内容执行完了,再执行函数b,然后你就收到一条信息了。

    这里必须清楚一点:函数b是你以参数形式传给函数a的,那么函数b就叫回调函数。

     也许有人有疑问了:一定要以参数形式传过去吗,我不可以直接在函数a里面调用函数b吗?确实可以。求解中。

    解惑:如果你直接在函数a里调用的话,那么这个回调函数就被限制死了。但是使用函数做参数就有下面的好处:当你a(b)的时候函数b就成了回调函数,而你还可以a(c)这个时候,函数c就成了回调函数。如果你写成了function a(){...;b();}就失去了变量的灵活性。

    上一段代码大家可以跑一下:

    function a(callback)
    {   
        alert("我是parent函数a!");
        alert("调用回调函数");
        callback();
    }
    function b(){
    alert("我是回调函数b");
      
    }
    function c(){
    alert("我是回调函数c");
      
    }
      
    function test()
    {
        a(b);
       a(c);
    }

    根据代码示例加深理解

    问题:

    现在有函数如下定义:
    function A(a,callback){
    ....
    }
    function B(){
    ....
    }
    则可以有如下调用
    A(a,B);来实现回调。
    现在我希望传给B一个参数c,即实现类似于:  A(a,B(c)); 的效果,应该如何实现?

    function A(a,callback){ 
    var b=callback; 
    alert(a+b); 
    } 
    function B(c){ 
    return (-c); 
    } 
    A(4,B(3))

    根据jquery的ajax请求常见的场景再去写一个示例:

    我包装了jQuery的ajax方法:

    function doAjax(u,param,callback){
          $.ajax({
                type:'POST',
                url:u,
                data:param,
                success:callback
          });
    }
     
    function showAlert(data){
         alert(data);
    }
     
    

    比如这样调用 doAjax("url","id=1",showAlert);
    $.ajax 在success后,会返回一个data到showAlert中,显示出来 ,没有问题.
    可是,当我想多传一个参数给showAlert时怎么写?
    写成doAjax("server.php","id=12&type=1",showAlert("hi",data))、或者把上面的success:callback 写成success:callback(msg,data)显然都不行,如之奈何?

    其实很简单,很多方法都定义了回调函数,回调函数也是函数,就是说不管怎么传,只需要是个函数类型即可。写法如下:

    方式1,
    doAjax(参数1,参数2,function(request,opts){
             callback(request,opts,agrs);
     });
    function callback(request,opts,args){
                   
    };
    方式2,
    var args=N;
    doAjax(参数1,参数2,function(request,opts){
           var X=N;
           回调函数代码块..
           和以上几乎一样,看个人编码方式选择。     
    });

    再看一个从父函数传递给回调函数的示例

    function e(m,n,Callback){  
        var d = m+n;  
        alert("一个从父函数e 产生的参数将要被传递给回调函数 ,这个参数是:"+d);  
         
        //这里才写你想调用的函数---参数要正确  
        Callback(d);   
    }  
    function callback(data){  
            alert("我是回调函数,我的名字叫:callback ,我接收到来自父函数的参数,参数是:"+data);   
    }
    e(1,2,callback)  

    最后

    感谢各位的阅读,如有问题麻烦及时指正出来。

     

    展开全文
  • 那么,回调函数是个什么鬼呢?它和函数到底有何异同?既然已经有了函数,为啥还非要生出个回调函数来?想必,小伙伴们在刚碰到这个概念的时候,都会被这些问题困扰。网上搜一搜,有很多相关的材料,但是未必透彻。我...

    在C/C++里面,函数的概念很好理解,就是把某个任务独立出来,封装在一起,然后给它取个名字,它可以有参数和返回值。那么,回调函数是个什么鬼呢?它和函数到底有何异同?既然已经有了函数,为啥还非要生出个回调函数来?想必,小伙伴们在刚碰到这个概念的时候,都会被这些问题困扰。网上搜一搜,有很多相关的材料,但是未必透彻。我觉得要真正理解一个概念,必须要先理解它存在的意义,也就是它为什么要存在,它能带来什么方便之处。在这一点上C++ Primer这本书写的还是比较到位的。仔细阅读之后,我把自己的心得写下来,供大家参考。


    首先,回调函数也是函数,就像白马也是马一样。它具有函数的所有特征,它可以有参数和返回值。其实,单独给出一个函数是看不出来它是不是回调函数的。回调函数区别于普通函数在于它的调用方式。只有当某个函数(更确切的说是函数的指针)被作为参数,被另一个函数调用时,它才是回调函数。就像给你一碗饭,你并不能说它是中饭还是晚饭一样,只有当你在某个时候把它吃掉了你才明确它是中饭还是晚饭(这个比喻貌似有点挫。领会精神就好,哈哈)。

    那么问题来了,为什么我们要把函数作为参数来调用呢,直接在函数体里面调用不好吗?这个问题问的好。在这个意义上,“把函数做成参数”和“把变量做成参数”目的是一致的,就是以不变应万变。形参是不变的,而实参是变的。唯一不同的是,普通的实参可以由计算机程序自动产生,而函数这种参数计算机程序是无法自己写出来的,因为函数本身就是程序(要是程序可以写程序的话那就是超级人工智能了),它必须由人来写。所以对于回调函数这种参数而言,它的“变”在于人有变或者人的需求有变。

    C++ Primer里面举了个例子就是排序算法。为了使排序算法适应不同类型的数据,并且能够按各种要求进行排序,机智的人类把排序算法做成了一个模版(在标准模版库STL里),并且把判断两个数据之间的“大小”(也可以是“字节数”,或者其他某种可以比较的属性)这个任务(即函数)当成一个参数放在排序算法这个函数的参数列表里,而把它的具体实现就交给了使用排序算法的人。这个判断大小的函数就是一个回调函数。比如我们要给某个vector容器里面的单词进行排序,我们就可以声明一个排序算法:

    void stable_sort(vector<string>::iterator iterBegin, vector<string>::iterator iterEnd, 
    bool (*isShorter)(const string &, const string &));

    其中前面两个是普通参数,即迭代器(用于标记vector容器里面元素的位置),而第三个参数isShorter就是回调函数。根据不同需求isShorter可以有不同的实现,包括函数名。比如:

    bool myIsShorter(const string &s1, const string &s2)
    {    
         return s1.size()<s2.size();
    }
    stable_sort(words.begin(),words.end(),myIsShorter);
    
    根据需求你也可以换一种方式来实现。注意,在传递myIsShorter这个参数时,只需写函数名,它代表函数指针。后面绝对不能加()和参数,绝对不能加()和参数,绝对不能加()和参数!因为那样是调用函数的返回值!两者天壤之别!在stable_sort运行时,当遇到需要比较两个单词的长短时,就会对myIsShorter进行调用,得到一个判断。在调用时,还必须把两个单词传递给isShorter供isShorter调用。所以说stable_sort调用了myIsShorter,而myIsShorter又调用了stable_sort给它的单词。它们相互调用。这就是“回调”这两个字的含义!

    虽然说形参不变,实参可变,以不变应万变。但是作为实参有一点还是不能变的,那就是实参的数据类型不能变。比如void foo(int i)这个函数里的参数i可以取1也可以取2,但是它必须是整型的。同样的,回调函数这种参数的类型也不能变。而函数的类型是由函数的参数类型和返回值类型决定的。比如前面提到的排序算法里面,isShorter这个回调函数的参数必须是两个const string类型,返回值必须是bool类型。所以在写回调函数时还是不能太任性,必须要查看一下调用该回调函数的函数的声明。[这里插播一段广告。假如回调函数本应该只有两个参数的,但是我想让它更普适一点,我想传三个参数给它,那怎么办呢?为了解决这个问题,C++标委又发明了lambda表达式和bind函数这两种方法。欲知详情请戳这里这里。]

    总之,所谓回调函数就是把函数当作参数使用。目的是使程序更加普适(正如活字印刷,把可能会“变”的字一个个分离开来,这样就可以任意组合,重复利用)。一般情况下,一个人的小规模程序用不着这种普适性,除非你想把它做成工具箱(比如游戏引擎),供他人使用。

    其实实现这种普适性还有其他方法,比如对虚函数进行重写(或者用纯虚函数。Objective C里面所有函数都是虚函数,而协议相当于纯虚函数)。这样同一个函数就可以有不同的实现。不同的合作者之间就可以通过这种虚函数“协议”进行合作。


    水平有限,如有不妥,欢迎拍砖!


    参考文献

    C++ Primer


    展开全文
  • 阻塞式回调函数和延迟式回调函数

    千次阅读 2020-04-30 18:16:57
    首先,有三种函数: 起始函数:大致可以等同于主函数 中间函数:中间函数把回调函数作为参数传递执行 ...在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做 触...
  • 通俗理解“回调函数

    万次阅读 多人点赞 2018-06-24 20:42:26
    我们先来看看回调的英文定义:A callback is a function that is passed as an argument to ...字面上的理解,回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的...
  • 1. 回调函数: 函数指针的调用,即是一个通过函数指针调用的函数; 如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,就说这是回调函数。 In computer programming, ...
  • JS回调函数——简单易懂有实例

    万次阅读 多人点赞 2018-05-11 17:33:02
    初学js的时候,被回调函数搞得很晕,现在回过头来总结一下什么是回调函数。什么是JS?(点击查看) 下面先看看标准的解释: <script language="javascript"> 02 function SortNumber( obj, func ) // ...
  • 在C++编程,尤其时写Qt程序时,需要大量使用回调函数,在网上也有很多大牛对回调函数的使用进行了讲解,但是很多都是针对某一个或者两个特定的类型的。我在这篇文章中对网上关于回调函数的使用进行了归纳总结,并且...
  • 一、如何在一个callback回调函数中调用另一个回调函数   网上找了好多帖子,都是在答非所问,我最终失去了耐心,自己去写个小demo碰碰运气吧,然后非常幸运,我解决了,哈哈,看下面的小案例。 图形界面非常简单,...
  • js 彻底理解回调函数

    万次阅读 多人点赞 2017-02-10 13:53:58
    一、前奏在谈回调函数之前,先看下下面两段代码: 不妨猜测一下代码的结果。function say (value) { alert(value); } alert(say); alert(say('hi js.'));如果你测试了,就会发现:只写变量名 say 返回的将会是 say...
  • C++回调函数使用心得

    万次阅读 多人点赞 2018-11-11 15:23:52
    C++回调函数使用心得前言回调函数也是普通函数C回调函数C++回调函数使用场景描述C++回调函数定义静态成员函数访问非静态成员函数的方法回调对象更复杂的讨论 前言 关于C++回调函数的介绍网上有很多,要知道它的...
  • 关于jquery中的ajax调取数据成功回调函数,失败回调函数,及跨域的问题

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 789,075
精华内容 315,630
关键字:

回调函数