回调函数在程序开发中是一个重要的知识点,是开发出优质代码不可不掌握的技能。曾经你有没有过这样的感受,总是听人讲回调函数这个概念,听上去好像逼格还挺高的,可这玩意到底是个啥函数?到底怎么用?有啥牛的地方呢?那我们今天就来聊聊回调函数。
01 函数指针
说起回调函数的话,那就不得不说说他的好兄弟函数指针。这是一对非常要好的CP。函数指针:指向函数的指针变量。因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。函数指针有两个用途:调用函数和做函数的参数。那么我们看下函数指针在程序中是如何定义的:
int(*p)(int, int);这个语句就定义了一个指向函数的指针变量 p。首先它是一个指针变量,所以要有一个“*”,即(*p);其次前面的 int 表示这个指针变量可以指向返回值类型为 int 型的函数;后面括号中的两个 int 表示这个指针变量可以指向有两个参数且都是 int 型的函数。所以合起来这个语句的意思就是:定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。p 的类型为 int(*)(int,int)。那么函数指针的定义方式为: 函数返回值类型 (* 指针变量名) (函数参数列表);我们还可以使用typedef定义一个函数指针的类型。从而实现对函数指针的名称进行一定的简化。如:typedef void (*FuncPtrType)(); 此时我们将FuncPtrType定义为了一个函数指针类型,接下来我们可以使用这个类型来定义变量:FuncPtrType funcPtr; 那么funcPtr 就是FunPtrTyped类型的一个函数指针了.
说了半天,函数指针和回调函数到底啥关系呢?这里按下不表,且听后面分解。简单复习了一下函数指针,下面言归正传,我们主要从以下三个方面来认识一下回调函数。 1、啥玩意?(定义) 2、咋整?(如何使用) 3、有啥用?(作用、特点)
02 啥玩意?
根据网上可以找到的比较官方的定义:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。这样的解释,整体上也能够理解,但是不是还是有点云里雾里的感觉?再看下面这个:
这个是知乎里面关于“回调函数是什么”这个问题的一个高赞回答。讲的生动、形象。把整个回调函数使用过程中的概念和实际生活中的一些行为进行了非常贴切的比喻。但我还是觉得有些迷糊。不知道具体的函数该怎么写,怎么调用,怎么就触发了回调。通过查阅资料以及自己的理解,我大概明白回调函数的概念了。我觉得上面的比喻不够贴切,所以试着把上面的例子进行了一个修改,来说明啥是回调函数以及它是如何工作的。例子如下:“你打电话给蛋糕店店员,表示要订了一个蛋糕,制作蛋糕需要等待一段时间完成后才能拿到。于是你把你家的地址告诉了店员。店员把它记录到本子上,并告知你做好后送货上门。蛋糕做好后,店员根据你留下的地址,把蛋糕给你送上门。” 我们来分析下。首先,这个店员手里的记录本是一个注册函数。然后,你把家庭地址告知了店员,这是注册(登记)回调函数。其次,等待一段时间蛋糕制作完成,这个是触发调用回调函数的条件。接着,店员根据你留下的地址,把蛋糕给你送货上门,这个是调用回调函数。最后,你拿到的蛋糕,甚至别的什么东西,都可以是回调函数中的内容。
03 咋整?
看下面一段简单的代码:
#includeint MyCallback_1() // Callback Function 1{ printf("this is Callback_1 "); return 0;}int MyCallback_2() // Callback Function 2{ printf("this is Callback_2 "); return 0;}int MyCallback_3() // Callback Function 3{ printf("this is Callback_3 "); return 0;}int main(){ printf("Entering Main Function. "); Handle(MyCallback_1);//注册回调函数1 Handle(MyCallback_2);//注册回调函数2 Handle(MyCallback_3);//注册回调函数3 printf("Leaving Main Function. "); return 0;}
主函数中回调函数的注册过程与回调函数本身在一个文件,相当于上面提到的买蛋糕的客户打电话订蛋糕以及在家里收到蛋糕这两个行为都是在自己家里完成的。注册回调函数的原型以及触发调用回调函数的机制在另外一层,相当于蛋糕店店员的记录本以及蛋糕制作完成后的送货。请结合我上面的论述体会再体会一下。
04 有啥用?
回调函数的一个最大的妙处就是解耦。所以我们把蛋糕店及蛋糕店店员的行为看作是模块A,客户及客户的行为看作模块B。这两个应该分别属于不同的文件。并且蛋糕店是服务于客户的,在程序架构中,也就是模块B是模块A的上层。为了减少模块之间的耦合,所以通过给蛋糕店打电话(调用注册函数,把回调函数的地址赋值给函数指针),而不是我亲自去蛋糕店(把模块B定义的函数让模块A调用),等到蛋糕做好了,就触发了调用回调函数的条件。然后一定是店员根据留下的地址送货上门。这个就是回调的过程,也就是don't call me ,i will call you !然后你拿到蛋糕以及对蛋糕如何处理都是属于回调函数。再扩展一步,你可以拿到的是蛋糕,也可以拿到的是披萨、面包、法棍、甚至什么穿的用的。当然,一万个读者有一万个哈姆雷特。每个人的理解不同,有人善于理解中规中矩的官方定义,有的人善于类比。任何一个概念如果生搬硬套地用一个例子来说明,或许会有牵强的成分。所以选择适合自己的就好。
这个文章是我进行过深度思考写出来的,如果对你有帮助的话,欢迎点赞、分享、收藏、在看。