深入浅出话回调(上)
小序
在团队的欢笑声中结束了一天紧张的工作,看着新Build出来的软件,想到过几天就要拿给客户去看了,心中有一种说不出的喜悦——我想那感觉应该跟自己家的女儿就要去见婆家有些许相似吧——尽管这小姑娘还事着不少小脾气,还会时不时地因为不知道什么原因撒个娇、耍个赖什么的……
先不去想这些了,收了思绪,回到自己温馨的小世界里吧。Blog就是日记,日记就是内心,荒芜的Blog意味着荒芜的内心——我的Blog已经荒芜的太久了。有些朋友问我是不是还会写那些逗乐的文字并把技术知识掺合在里面,我说:拿不准主意。不是不想写,是真的拿不出什么激情来了。可能是人渐渐长大了、变老了吧(感觉这几个月来老了好几岁),心态也慢慢变得越来越沉实、越来越平和。
今天写点什么呢?唔~~写写回调函数吧,很有用的技术,理解了这个技术对理解Win32的WinProc、理解Java/ActionScript/C#等的委托、事件处理机制都大有裨益。
拧开台灯,调节到一个合适的亮度,让屋子里的每一个Object都蒙上一层浅浅的金黄;再沏上一杯清淡的绿茶——婀娜的蒸汽在灯光中轻柔地飘舞,把沁人的香气抛送到空中,当茶叶旋转着在水中上下嬉戏时,我们的程序之旅开始了……
正文
一.什么是回调(Callback)函数
回调函数(Callback Function)是怎样一种函数呢?
函数是用来被调用的,我们调用函数的方法有两种:
l 直接调用:在函数A的函数体里通过书写函数B的函数名来调用之,使内存中对应函数B的代码得以执行。这里,A称为“主叫函数”(Caller),B称为“被叫函数”(Callee)。
l 间接调用:在函数A的函数体里并不出现函数B的函数名,而是使用指向函数B的函数指针p来使内存中属于函数B的代码片断得以执行——听起来很酷,是吧。
比起直接调用来,间接调用的确麻烦,那为什么还要使用间接调用呢?原因很简单——直接调用把函数名都写进函数体了,经过编译器那么一编译,板上钉钉,A注定调用的是B了,这样的程序只能按照程序员事先设计好的流程执行下去,太呆板了。此时,间接调用的巨大灵活性就显现出来了。想一想,如果p是函数A的一个参数(参数是变量,是变量就可以变吗!),那么程序的最终用户完全可以通过操作来改变p的指向——这样,A在通过p调用函数的时候就有机会调用到不同的函数,这样程序的实用性和扩展性就强多了。
如果你能明白上面一段话,OK,那么你已经明白回调函数的75%了——因为被间接调用的函数就是回调函数本身,而间接调用是使用回调函数的“第二步”。
“第一步”又是什么呢?让我们仔细想想,A通过p来调用B(或者调用其它函数),p是一个指针——空指针是不能用的!所以,“第一步”就是声明函数指针变量p并且为它赋值——即将p绑定到一个将被间接调用的函数上。
这样,使用回调函数的完整流程就成了这样:
声明函数指针p à 向函数指针p赋值,使之指向函数B à 把p作为参数传给函数A à 函数A通过指针p调用函数B à 函数B的函数体得以执行。
现在一个令人迷惑的问题摆在眼前了——Callback这个词里有一个back,上面这个过程哪里体现出“回”了呢?实际上是这样的:
函数指针p的来路我们不必去追究——它可能是一个全局变量(大部分情况下是这样),也可能是一个临时变量——对p赋值却是一件着实重要的事情,除了在声明它的时候对它初始化,我们只能在某个函数中去执行对p进行赋值的操作了,我们姑且管这个函数叫K。现在闭上眼睛跟我想,函数K和函数B处在同一个程序模块(比如一个类或者一个DLL)Module_1中,而函数A处在另一个程序模块Modules_2中,把p想象成一条短信。Module_1发短信p给Module_2,短信p的内容是“函数B的内存地址是0XFF1314521EE”,Module_2收到短信后使用这个地址,结果相当于给Module_2“回了个电话”。所以,一个“回”字,是站在包含了函数K和B的模块Module_1的角度上去看而得出的结果。
现在想想,台湾的IT同仁保留了Callback一词的英语词条直译,就叫“回呼函数”,真是高明。将Callback特化成计算机专业术语、叫做“回调”真不知道是谁的主意:p
二.小牛试刀
咱们老规矩,贴段代码放在这儿供大家拍砖玩儿。
//----------------------------------------------
// 水之真谛
// http://blog.csdn.net/FantasiaX
//----------------------------------------------
#include <iostream>
// 声明一个函数指针,返回值为void,接受一个整形参数
typedef void (*FunctionPointer)( );
void Function_A( );
void Function_B( FunctionPointer );
void Function_C( );
void Function_D( );
// 回调过程的原始发起者
void Function_A( )
{
FunctionPointer p = 0;
std::cout << "Please give me a choice, 1 or 2." << std::endl;
int choice = 0;
std::cin >> choice;
if(choice == 1)
p = &Function_C;
else if(choice ==2 )
p = &Function_D;
else
return;
Function_B( p );
}
// 回调过程中的“主叫函数”,Caller
void Function_B(FunctionPointer p)
{
p( );
}
// 回调过程中的“被叫函数”,Callee
// 也就是回调函数本身,回调函数-1
void Function_C( )
{
std::cout << "I am Function_C. Merry Christmas!" << std::endl;
}
//回调函数-2
void Function_D( )
{
std::cout << "I am Function_D. Happy New Year!" << std::endl;
}
int main(int argc, char *argv[])
{
Function_A( );
return 0;
}
TO BE CONTINUE
法律声明:本文章受到知识产权法保护,任何单位或个人若需要转载此文,必需保证文章的完整性(未经作者许可的任何删节或改动将视为侵权行为)。若您需要转载,请务必注明文章出处为CSDN以保障网站的权益;请务必注明文章作者为刘铁猛(http://blog.csdn.net/FantasiaX ),并向liutm@beyondsoft.com发送邮件,标明文章位置及用途。转载时请将此法律声明一并转载,谢谢!
分享到:
相关推荐
本书《深入浅出Node.js》从多个角度细致深入地探讨了Node.js的内部工作机制和应用实践。 首先,Node.js的核心优势之一是其异步I/O模型。传统的Web应用模型中,对于I/O操作(如读写数据库、文件系统和网络请求等),...
根据提供的文件信息,我们可以推断出这是一篇关于获取《深入浅出JavaScript》电子书的信息。下面我们将基于这个前提,围绕“深入浅出JavaScript”这一主题进行知识点的展开。 ### 深入浅出JavaScript概览 #### 一...
### JavaScript深入浅出 #### 一、前言与概述 《JavaScript深入浅出》是一本致力于帮助读者深入了解JavaScript编程语言的书籍。JavaScript作为现代Web开发不可或缺的一部分,其重要性不言而喻。本书通过深入浅出的...
《竹林蹊径:深入浅出Windows驱动开发源码》是一部专为想要深入了解Windows驱动程序开发的工程师量身打造的指南。这本书通过丰富的实例和源码解析,带领读者步入驱动开发的世界,帮助他们掌握驱动程序的基本原理和...
《深入浅出MFC》读书笔记1主要涵盖了MFC(Microsoft Foundation Classes)的基础知识,特别是对Win32程序开发流程的解析。MFC是微软提供的一套C++库,用于简化Windows应用程序的开发,它封装了Windows API,使得...
本书内容深入浅出,先讲述持久层设计与ORM,再由Hibernate概述、Hibernate基础Hibernate高级特性顺序展开,直至Hibernate实战,重点讲述了Hibernate的基础语法、基础配置、O/R映射、数据关联、数据检索、HQL实用技术...
### 深入浅出MFC—笔记关键知识点解析 #### 一、Windows消息机制与回调函数 在深入了解MFC之前,我们首先需要理解Windows操作系统的基本工作原理,尤其是消息驱动模型以及回调函数的概念。 **1.1 Windows消息机制...
本书内容深入浅出,先讲述持久层设计与ORM,再由Hibernate概述、Hibernate基础Hibernate高级特性顺序展开,直至Hibernate实战,重点讲述了Hibernate的基础语法、基础配置、O/R映射、数据关联、数据检索、HQL实用技术...
### 深入浅出MFC学习笔记:关键知识点解析 #### 一、Windows消息机制与回调函数 在深入探讨MFC之前,我们先来理解Windows操作系统中的消息机制及其核心组成部分——回调函数。 ##### 1.1 Windows消息机制概述 ...
总的来说,深入浅出JavaScript源代码的学习涵盖了语言基础、面向对象、异步编程、DOM操作以及新特性的应用等多个方面。通过系统学习和实践,开发者不仅能理解JavaScript的工作原理,还能掌握编写高效、可维护代码的...
《深入浅出JavaScript》是一本深受开发者欢迎的书籍,它以清晰易懂的方式全面解析了JavaScript这门重要的编程语言。JavaScript,简称JS,是互联网上最广泛使用的脚本语言,用于实现网页的动态效果和交互功能。这本书...
《深入浅出JavaScript源代码》是一本旨在帮助开发者深入理解JavaScript核心机制的书籍,通过源代码分析来提升对语言本质的理解。这个压缩包包含了这本书籍中的源代码示例,为学习者提供了实际操作和研究的素材。 在...
《深入浅出Windows驱动开发》是一本专注于Windows操作系统驱动程序设计与实现的专业书籍。通过学习这本书,读者可以深入了解Windows驱动程序的工作原理、开发流程以及调试技巧。以下将详细阐述其中涉及的知识点: 1...
"深入浅出JavaScript" 是一套专门针对JavaScript初学者和进阶者设计的学习资料,旨在帮助读者全面理解这门语言,并通过实例加深对概念的理解。这本书的中文版——"Head First JavaScript",以其独特的视觉和互动式...
本书内容深入浅出,先讲述持久层设计与ORM,再由Hibernate概述、Hibernate基础Hibernate高级特性顺序展开,直至Hibernate实战,重点讲述了 Hibernate的基础语法、基础配置、O/R映射、数据关联、数据检索、HQL实用...
《深入浅出Linux设备驱动编程》是一本专为IT专业人士准备的深度技术指南,它涵盖了Linux设备驱动开发的多个核心领域。这本书旨在帮助读者理解如何有效地编写和优化Linux系统下的设备驱动程序,以实现硬件与操作系统...
《深入浅出JAVASCRIPT 中文版》这本书正是为了帮助读者深入理解和掌握这种语言而编写的。 首先,JavaScript的基础知识包括变量、数据类型和操作符。变量是存储数据的地方,而数据类型则定义了变量能存储的数据种类...