当前位置:C++技术网 > 资讯 > 函数调用与回调函数的设计原理的深入对比分析

函数调用与回调函数的设计原理的深入对比分析

更新时间:2015-06-26 21:21:23浏览次数:1+次

    为什么我会想起分析这个话题呢?这是在我工作时与一同事在协调功能对接时用到了回调函数,让我受益匪浅。很久了,但是这个问题在昨天看COM编程的书中,里面又用到了回调函数机制。之前说总结一下的,后来一忙,就忘了。现在就提到这个了,我也发现了回调函数的魅力了,所以,赶紧总结一篇,与君共享。
    一开始我们学习回调函数,总是认知在系统提供回调函数,调用我们的函数,从而达到让我们可以控制部分行为。然而,工作时需要使用到这个机制了。因为同事做底层基础功能,所以,与我的功能模块交互。为了让基础库更通用,也让上层可以自由定制行为,最终采用了回调函数机制。而一开始,我只是觉得应该用函数调用来实现,后来明白后,才发现函数调用的不足之处。下面我来介绍一下函数调用和回调函数的机制对比,让你发现回调函数的魅力所在。不再会对回调函数又恐惧感,反而是无限的激动。
    我们以两个函数来说明。

/* - 第一种形式即函数调用。- */
void A()
{
    ...
    B();
    ...
}

     省略号代表其中执行了一些操作,然后调用B函数,最后在执行一些操作,A函数就执行完毕。这是简单的分析。让我们进一步分析。
    在外部函数调用A函数时,也就是执行A函数的代码。执行代码的意义,就是CPU会执行A函数生成的机器代码。从另一个角度来看,也就是,A函数此时控制了CPU的执行流程,即A函数有了控制权。在执行一段代码后,调用了B函数。此时,控制权就转移到B去了。最后,B函数返回后,控制权有返回到了A。最后执行完剩余的代码。
    我们来看看第二种形式,即回调函数。

/* - 第二种形式即回调函数机制 - */
void A()
{
    ...
    pFunB();
    ...
} 
    PFUNB 为自定义的函数类型指针,定义如下:
typedef void (*PFUNB) () ;
PFUNB pFunB;
     这样定义下来后,PFUNB 就是B函数的函数指针类型,用PFUNB 定义一个函数变量pFun,在用pFun来执行函数调用。
    实际上,这样折腾一下,就成了回调函数。或许你现在还没有完全明白怎么回事。下面详细解释一下。
    pFun是函数指针了,自然就可以给函数指针赋值,赋的值就是一个函数名,即函数执行体的地址。这样被赋值后,通过函数指针调用函数和普通的B()调用没什么差别,但是却成了回调函数机制了。因为在调用函数的分析过程中,这种形式也是一样的。而它的特别之处就是下面介绍的这样了,这才是回调函数的魅力所在。如果没有这个魅力,微软也不会使用那么多回调函数机制给我们提供定制行为的机制。
    回调函数机制设计的目的就是,在底层提供基础的行为时,在不改动底层代码,就可以向上提供灵活的行为定制能力。怎么说呢?一般来说,在设计一个基础库时,要考虑到,基础库是频繁被使用,且需要非常健壮的。还有一点非常重要,就是,基础库不能经常被改动,否则一个问题会对软件造成致命性的损伤。就好比,微软系统一个通用性的漏洞,比如基础层面的核心层的代码有一个漏洞,那么会导致所有产品都陷入危机。这些影响是相当恶劣的。所以,基础库不能轻易改动,一旦实现后,经过严格测试,以后不会轻易修改。
    然而,基础库要让上层的功能实现灵活的定制,如果采用函数调用,那是绝对不现实的。因为底层库要知道上层函数的信息,要让上层函数提供完整的代码实现,不可能。因为在底层库开发时或者运行时,不会知道上层会有什么东西,更不用说要提供上层的代码实现。如果不提供,那么函数调用就无法执行。代码实现就是在调用函数时,知道函数中的代码是如何执行的。也就是,有函数的实现。因为底层库使用时,作为基础,肯定是最先执行的,此时上层函数都不知道在哪,此时又怎么知道上层的代码呢?如果你说代码可以先存储给基础库用,那么这还叫基础库吗?这样你存储的函数代码就成了基础库的基础了。函数调用就是这样的,它需要被调用的函数事先存在,否则无法调用。
    但是思维稍微一变,事情就是柳暗花明又一村了。既然无法提供上层函数的代码实现,那么可以提供一个函数的指针吧。看看吧,这也是指针的厉害之处。
    为什么提供指针可以呢?C/C++提供了对指针的原始操作支持。此时,不需要提供上层函数的代码实现。只需要在底层库运行时通过此指针调用此函数即可。假如通过指针调用此上层函数时,函数未实现,或者指针值错误,那么就会导致崩溃。而正确的情况下,就是底层库运行此A函数前,已经对函数指针正确的赋值了。在A执行时就可以正确的调用到函数执行。这是在基础上的技术实现。
    而另一方面,假如基础库要调用的函数是同级的函数,同时是可以提供函数实现,因此也可以顺利的调用。但是,问题来了。假如有很多的功能在日后不断的提出,此时你就需要修改基础库来提供支持行为定制或者功能定制,此时你是不是很苦恼。因为经常修改基础库会带来很坏的影响。所以给定制和扩展性带来了极大的不便。
    而在基础库中,以函数指针的形式提供调用,则可以随时定制函数的行为。只要按照约定,在执行基础库的此函数之前,给函数指针正确的赋值,用你的定制的函数来赋值,就可以在基础库函数中使用你的函数的执行行为,从而达到行为定制,且扩展性极好。在扩展功能和定制行为时,根本就不用再修改基础库的这个函数了。从而,也可以保证基础库不会在经常的修改中引入错误。
    通过以上的对比分析,我想,此时你应该对回调函数机制有了充分的了解吧。如果发现有问题,请提出,如果又不懂,也记得提出哦。