小编推荐一个学 C 语言 / C++ 的学习裙 六九九, 四七零, 五九六 , 无论你是大牛还是小白, 是想转行还是想入行都可以来了解一起进步一起学习! 裙内有开发工具, 很多干货和技术资料分享!
1. 什么是回调函数?
回调函数, 光听名字就比普通函数要高大上一些, 那到底什么是回调函数呢? 恕我读得书少, 没有在那本书上看到关于回调函数的定义我在百度上搜了一下, 发现众说纷纭, 有很大一部分都是使用类似这么一个场景来说明: A 君去 B 君店里买东西, 恰好缺货, A 君留下号码给 B 君, 有货时通知 A 君感觉这个让人更容易想到的是异步操作, 而不是回调另外还有两句英文让我印象深刻: 1) If you call me, I will call you back; 2) Don't call me, I will call you. 看起来好像很有道理, 但是仔细一想, 普通函数不也可以做到这两点吗? 所以, 我觉得这样的说法都不是很妥当, 因为我觉得这些说法都没有把回调函数的特点表达出来, 也就是都看不到和普通函数到底有什么差别不过, 百度百科的解析我觉得还算不错 (虽然经常吐槽百度搜索...): 回调函数就是一个通过函数指针调用的函数如果你把函数的指针(地址) 作为参数传递给另一个函数, 当这个指针被用来调用其所指向的函数时, 我们就说这是回调函数
下面先说说我的看法我们可以先在字面上先做个分解, 对于回调函数, 中文其实可以理解为这么两种意思: 1) 被回调的函数; 2) 回头执行调用动作的函数那这个回头调用又是什么鬼?
先来看看来自维基百科的对回调 (Callback) 的解析: In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback. 也就是说, 把一段可执行的代码像参数传递那样传给其他代码, 而这段代码会在某个时刻被调用执行, 这就叫做回调如果代码立即被执行就称为同步回调, 如果在之后晚点的某个时间再执行, 则称之为异步回调关于同步和异步, 这里不作讨论, 请查阅相关资料
再来看看来自 Stack Overflow 某位大神简洁明了的表述: A "callback" is any function that is called by another function which takes the first function as a parameter 也就是说, 函数 F1 调用函数 F2 的时候, 函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针, 在函数 F2 执行的过程中, 函数 F2 调用了函数 F3, 这个动作就叫做回调(Callback), 而先被当做指针传入后面又被回调的函数 F3 就是回调函数到此应该明白回调函数的定义了吧?
2. 为什么要使用回调函数?
很多朋友可能会想, 为什么不像普通函数调用那样, 在回调的地方直接写函数的名字呢? 这样不也可以吗? 为什么非得用回调函数呢? 有这个想法很好, 因为在网上看到解析回调函数的很多例子, 其实完全可以用普通函数调用来实现的要回答这个问题, 我们先来了解一下回到函数的好处和作用, 那就是解耦, 对, 就是这么简单的答案, 就是因为这个特点, 普通函数代替不了回调函数所以, 在我眼里, 这才是回调函数最大的特点来看看维基百科上面我觉得画得很好的一张图片
Callback
下面以一段不完整的 C 语言代码来呈现上图的意思:
- #include
- #include // 包含 Library Function 所在读得 Software library 库的头文件
- int Callback() // Callback Function
- {
- // TODO
- return 0;
- }
- int main() // Main program
- {
- // TODO
- Library(Callback);
- // TODO
- return 0;
- }
乍一看, 回调似乎只是函数间的调用, 和普通函数调用没啥区别, 但仔细一看, 可以发现两者之间的一个关键的不同: 在回调中, 主程序把回调函数像参数一样传入库函数这样一来, 只要我们改变传进库函数的参数, 就可以实现不同的功能, 这样有没有觉得很灵活? 并且丝毫不需要修改库函数的实现, 这就是解耦再仔细看看, 主函数和回调函数是在同一层的, 而库函数在另外一层, 想一想, 如果库函数对我们不可见, 我们修改不了库函数的实现, 也就是说不能通过修改库函数让库函数调用普通函数那样实现, 那我们就只能通过传入不同的回调函数了, 这也就是在日常工作中常见的情况现在再把 main()Library()和 Callback()函数套回前面 F1F2 和 F3 函数里面, 是不是就更明白了?
明白了回调函数的特点, 是不是也可以大概知道它应该在什么情况下使用了? 没错, 你可以在很多地方使用回调函数来代替普通的函数调用, 但是在我看来, 如果需要降低耦合度的时候, 更应该使用回调函数
3. 怎么使用回调函数?
知道了什么是回调函数, 了解了回调函数的特点, 那么应该怎么使用回调函数? 下面来看一段简单的可以执行的同步回调函数代码
小编推荐一个学 C 语言 / C++ 的学习裙 六九九, 四七零, 五九六 , 无论你是大牛还是小白, 是想转行还是想入行都可以来了解一起进步一起学习! 裙内有开发工具, 很多干货和技术资料分享!
- #include
- int Callback_1() // Callback Function 1
- {
- printf("Hello, this is Callback_1");
- return 0;
- }
- int Callback_2() // Callback Function 2
- {
- printf("Hello, this is Callback_2");
- return 0;
- }
- int Callback_3() // Callback Function 3
- {
- printf("Hello, this is Callback_3");
- return 0;
- }
- int Handle(int (*Callback)())
- {
- printf("Entering Handle Function.");
- Callback();
- printf("Leaving Handle Function.");
- }
- int main()
- {
- printf("Entering Main Function.");
- Handle(Callback_1);
- Handle(Callback_2);
- Handle(Callback_3);
- printf("Leaving Main Function.");
- return 0;
- }
运行结果:
- Entering Main Function.
- Entering Handle Function.
- Hello, this is Callback_1
- Leaving Handle Function.
- Entering Handle Function.
- Hello, this is Callback_2
- Leaving Handle Function.
- Entering Handle Function.
- Hello, this is Callback_3
- Leaving Handle Function.
- Leaving Main Function.
可以看到, Handle()函数里面的参数是一个指针, 在 main()函数里调用 Handle()函数的时候, 给它传入了函数 Callback_1()/Callback_2()/Callback_3()的函数名, 这时候的函数名就是对应函数的指针, 也就是说, 回调函数其实就是函数指针的一种用法现在再读一遍这句话: A "callback" is any function that is called by another function which takes the first function as a parameter, 是不是就更明白了呢?
4. 怎么使用带参数的回调函数?
眼尖的朋友可能发现了, 前面的例子里面回调函数是没有参数的, 那么我们能不能回调那些带参数的函数呢? 答案是肯定的那么怎么调用呢? 我们稍微修改一下上面的例子就可以了:
- #include
- int Callback_1(int x) // Callback Function 1
- {
- printf("Hello, this is Callback_1: x = %d", x);
- return 0;
- }
- int Callback_2(int x) // Callback Function 2
- {
- printf("Hello, this is Callback_2: x = %d", x);
- return 0;
- }
- int Callback_3(int x) // Callback Function 3
- {
- printf("Hello, this is Callback_3: x = %d", x);
- return 0;
- }
- int Handle(int y, int (*Callback)(int))
- {
- printf("Entering Handle Function.");
- Callback(y);
- printf("Leaving Handle Function.");
- }
- int main()
- {
- int a = 2;
- int b = 4;
- int c = 6;
- printf("Entering Main Function.");
- Handle(a, Callback_1);
- Handle(b, Callback_2);
- Handle(c, Callback_3);
- printf("Leaving Main Function.");
- return 0;
- }
运行结果:
- Entering Main Function.
- Entering Handle Function.
- Hello, this is Callback_1: x = 2
- Leaving Handle Function.
- Entering Handle Function.
- Hello, this is Callback_2: x = 4
- Leaving Handle Function.
- Entering Handle Function.
- Hello, this is Callback_3: x = 6
- Leaving Handle Function.
- Leaving Main Function.
可以看到, 并不是直接把 int Handle(int (*Callback)()) 改成 int Handle(int (*Callback)(int)) 就可以的, 而是通过另外增加一个参数来保存回调函数的参数值, 像这里 int Handle(int y, int (*Callback)(int)) 的参数 y 同理, 可以使用多个参数的回调函数
本人不才, 关于回调函数的探讨就只能到此为止了, 欢迎各路大神指正
来源: http://www.jianshu.com/p/53a3d317b0b8