回调函数技术广泛运用在动态库开发(或者类库)中,是使软件模块化的重要手段。回调函数可以看作是一种通知和实现机制,用于控制反转,即模块A调用模块B时,模块B完成一定任务后反过头来调用模块A。在被调用方代码改变(功能变化)时,调用者代码保持不变。这种方式对应了一个经典的软件设计原则--开闭原则:软件模块对修改关闭,对添加新代码开放,也就是说,增加新功能时,增加新代码,但不修改老代码。由于可以动态加载dll,只要新的dll(接口与旧的dll相同)覆盖老dll,就实现了系统升级。

一般地,代码调用有三种方式,见图示:

同步调用最常见,它是单向调用,调用方A阻塞等待调用方B完成后返回。

回调是双向调用,被调用接口被调用时随后会调用调用方的接口。如果把调用方A称为高层,调用方B称为底层,回调就是高层调用底层,底层再回过头来调用高层的过程。这也就是回调得名的原因吧。从这个过程来看,回调接口由被调用方提供,调用方定义相同的接口原型和实现,并注册到被调用方提供的登记入口上,回调的真正实现在调用方。

异步调用:类似于消息或事件通知机制,在接口的服务收到被调用的消息或事件时,会主动调用调用者的接口,方向正好与同步调用相反。当然,实现异步调用的代码比同步调用要复杂的多。

当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function,中间函数)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。见下面的图示:

可以看到,回调函数由应用层提供,与应用处于同一抽象层。

中间函数与回调函数是回调的两个必要部分,不过人们常常忽略了回调里的第三位要角,就是中间函数的调用者。在一般简单的例子中,这个调用者可以和程序的主函数等同起来,但在模块化编程中,也许这个调用者是程序中的某个模块,模块中的某个函数调用了中间函数。为了表示区别,把它成为起始函数。

很多文章在解释回调概念时,都会提到这么一句话:“if you call me,i will call you back”。回调不是中间函数、回调函数两方的互动,而是起始函数、中间函数、回调函数的三方互动。给中间函数传入什么样的回调函数,是起始函数决定的。有了这层理解,在代码中实现回调时才不容易混淆出错。

回调技术最主要的用途是解耦,假设有两个模块A和B,如果模块A依赖模块B,在A中调用B,依赖是单方向的,即A依赖于B,如果B又要通知A,B就对A产生了依赖,而且是双向依赖。现在我们要解耦,让依赖只是单方向的,做法是A依赖B,B依赖一个函数指针,这个指针可以来自于任何地方。这样B对A的调用就变为隐式调用,B对A不依赖,只是依赖一个函数接口,这个接口就是回调。这样就做到了不依赖实现,依赖接口。

上面的表述过程太过抽象,用生活中的例子来比喻回调技术吧。

打个比方,有家酒店不仅提供住宿服务还提供叫醒服务,叫醒服务内容是客服在规定的时间打电话到客房。旅客即可以选择睡到自己醒来,也可以选择睡到客服打电话叫醒自己。前者是睡觉(高层调用底层实现)醒来(高层自身实现)两个步骤,后者是登记叫醒服务(高层调用底层注册回调的接口),睡觉(高层调用底层实现)呼叫(底层通知高层)醒来(高层自身实现)三个步骤。旅客要享受叫醒服务,需要先告诉酒店,这个告诉的动作,就叫登记回调函数。多出来的登记动作和通知动作就是回调与一般的函数调用最大的不同。

回调机制提供了巨大的灵活性,比如上边的叫醒服务,如果酒店不仅提供打客房电话,还可以是工作人员敲房门,登记了不同的服务内容,享受到的服务也不同,这就是灵活性的好处。举一个编程上的例子,Win32 SDK编程中,操作系统提供了注册窗口过程函数的接口,不同的软件实现自己不同的窗口过程,并注册到操作系统,软件运行的行为也就此不同了。

回调机制落地,代码实现

1.最简单的C语言实现

void callback(int a)
{
cout<<"callback called with para="<<a<<endl;
} typedef void (*pfunc)(int);
void caller(pfunc p)
{
(*p)(1);
} int main(int argc, char* argv[])
{
caller(&callback);
}

2. C++  静态成员函数方式实现(公司实际项目中大量使用)

3. Sink方式

参考:

被误读了千年的回调函数--写得实在太好了

C++回调机制实现(转)

回调函数之同步调用、回调、异步调用

异步消息的传递-回调机制

C++的回调机制

C++回调:利用Sink 

C++面试基础之回调的更多相关文章

  1. 快速掌握JavaScript面试基础知识(三)

    译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...

  2. 一些iOS面试基础题总结

    一些iOS面试基础题总结 目录 多线程 AutoLayout objc_msgSend Runtime 消息转发 Category NSObject 与 objc_class Runloop Auto ...

  3. 10个经典的C语言面试基础算法及代码

    10个经典的C语言面试基础算法及代码作者:码农网 – 小峰 原文地址:http://www.codeceo.com/article/10-c-interview-algorithm.html 算法是一 ...

  4. iOS 面试基础题目

    转载: iOS 面试基础题目 题目来自博客:面试百度的记录,有些问题我能回答一下,不能回答的或有更好的回答我放个相关链接供参考. 1面 Objective C runtime library:Obje ...

  5. 快速掌握JavaScript面试基础知识(二)

    译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...

  6. 前端读者 | 前端面试基础手册(HTML+CSS)

    本文来自@羯瑞:希望前端面试基础手册能帮助要找工作的前端小伙伴~~ HTML 前端需要注意哪些SEO? 合理的title.description.keywords:搜索对着三项的权重逐个减小,titl ...

  7. 前端面试基础题:Ajax原理

    Ajax 的原理简单来说是在⽤户和服务器之间加了—个中间层( AJAX 引擎),通过XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后⽤ javascrip t 来操作 D ...

  8. Java 笔试面试 基础篇 一

    1. Java 基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法, 线程的语法,集合的语法,io 的语法,虚拟机方面的语法. 1.一个".java& ...

  9. java面试基础题(三)

    程序员面试之九阴真经 谈谈final, finally, finalize的区别: final:::修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承.因此 ...

随机推荐

  1. linux下进程绑定cpu情况查看的几种方法

    1.pidstat命令 查看进程使用cpu情况,如果绑定了多个cpu会都显示出来 pidstat -p `pidof 进程名` -t 1 2.top命令 (1)top (2)按f键可以选择下面配置选项 ...

  2. java集合类,HashMap,ArrayList

    集合类 Collection LinkedList.ArrayList.HashSet是非线程安全的, Vector是线程安全的; ArrayXxx:底层数据结构是数组,连续存放,所以查询快,增删慢. ...

  3. 5N - 考试排名

    C++编程考试使用的实时提交系统,具有即时获得成绩排名的特点.它的功能是怎么实现的呢? 我们做好了题目的解答,提交之后,要么“AC”,要么错误,不管怎样错法,总是给你记上一笔,表明你曾经有过一次错误提 ...

  4. HTTP知识点【总结篇】

    1.什么是HTTP协议? 客户端和服务器之间数据传输的格式规范.全拼:HyperText Transfer Protocol:超文本传输协议. 2.http协议是无状态协议?怎么解决无状态协议? 无状 ...

  5. CentOS6系统编译部署LAMP(Linux, Apache, MySQL, PHP)环境

    我们一般常规的在Linux服务器中配置WEB系统会用到哪种WEB引擎呢?Apache还是比较常用的引擎之一.所以,我们在服务器中配置LAMP(Linux, Apache, MySQL, PHP)是我们 ...

  6. CAS SSO单点登录框架学习

    1.了解单点登录  SSO 主要特点是: SSO 应用之间使用 Web 协议(如 HTTPS) ,并且只有一个登录入口. SSO 的体系中有下面三种角色: 1) User(多个) 2) Web 应用( ...

  7. Java获取请求主机真实ip

    一般情况下 getRemoteAddr()是可以正常使用的,代码如下: public String getIpAdress(HttpServletRequest request) { ip = req ...

  8. c语言相关概念

    2019-04-06 a文件 库是预编译的目标文件(object files)的集合,它们可被链接进程序.静态库以后缀为‘.a’的特殊的存档文件(archive file)存储. a文件转so文件:h ...

  9. 批处理-Java JDK环境变量配置

    setx /M JAVA_HOME "C:\Program Files\Java\jdk1.8.0_131" setx /M CLASSPATH ".;%%JAVA_HO ...

  10. Redis详细讲解(Redis原理,Redis安装,Redis配置,Redis使用,Redis命令)

    一.Redis介绍 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发 ...