您的位置:首页 > 其它

关于回调函数的个人理解

2015-07-29 18:16 218 查看
刚入程序员也不是很久,有些东西接触的也不是很多,今天和同事在模块间调用时发现了回调函数,由于之前这方面比较模糊,所以恶补一下并且发一篇个人博客帮助更多的新人理解回调函数。

大家一听到这个名字“回调函数”,就感觉会很难,其实则不然,当你真正的理解了原理你会发现其实回调函数也没什么,最简单的理解就是一个函数指针进行函数调用的过程。

网上有很多大牛们的解释,比如:

1. 为什么要用回调呢?

比如我要写一个子模块给你用, 来接收远程socket发来的命令.当我接收到命令后, 需要调用你的主模块的函数, 来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令, 我也不知道你的主模块是什么.cpp或者.h, 或者说, 我根本不用关心你在主模块里怎么处理它, 也不应该关心用什么函数处理它...... 使用回调!

2. 使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。

3. 回调函数,就是由你自己写的。你需要调用另外一个函数,而这个函数的其中一个参数,就是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可以在回调函数里完成你要做的事。

4. 回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。

看了这么多大牛们的解释,是不是还有点晕,其实我打一个不是很形象的比喻来说明什么是回调函数和回调函数的机制:有一对父子,父亲和孩子之间有个约定,约定是孩子出门父亲会给孩子钱,孩子每次回家要告诉父亲自己花了多少钱,某天孩子出门前父亲给了他一个钱包说里面的钱随便花,孩子带着钱包出门了,花了好多钱,晚上回家后把钱包还给父亲并告诉他自己花了多少钱。其实回调函数就好比这样。主函数给子函数一个回调函数,主函数不关心子函数去做什么,就好比父亲不关心孩子花钱做什么一样,但是主函数和子函数之间需要有一种约定,就是当子函数调回回调函数时的参数必须按照双方商议好,就好比孩子回家必须要告诉父亲花了多少钱一样。主函数对回调回来的参数进行处理,这就是回调函数的机制。其实仔细想一想,子函数其实就好比一个接口。

附一篇感觉写的很棒的博客地址:http://www.cnblogs.com/chenyuming507950417/archive/2012/01/02/2310114.html

(下面内容是转载上面地址的,如有侵权请联系本人,本文仅用于学习交流)

在理解“回调函数”之前,首先讨论下函数指针的概念。

函数指针

(1)概念:指针是一个变量,是用来指向内存地址的。一个程序运行时,所有和运行相关的物件都是需要加载到内存中,这就决定了程序运行时的任何物件都可以用指针来指向它。函数是存放在内存代码区域内的,它们同样有地址,因此同样可以用指针来存取函数,把这种指向函数入口地址的指针称为函数指针。

(2)先来看一个Hello World程序:

int main(int argc,char* argv[])
{
printf("Hello World!\n");
return 0;
}


然后,采用函数调用的形式来实现:

void Invoke(char* s);

int main(int argc,char* argv[])
{
Invoke("Hello World!\n");
return 0;
}

void Invoke(char* s)
{
printf(s);
}


用函数指针的方式来实现:

void Invoke(char* s);

int main()
{
void (*fp)(char* s);    //声明一个函数指针(fp)
fp=Invoke;              //将Invoke函数的入口地址赋值给fp
fp("Hello World!\n");   //函数指针fp实现函数调用
return 0;
}

void Invoke(char* s)
{
printf(s);
}


由上知道:函数指针函数的声明之间唯一区别就是,用指针名(*fp)代替了函数名Invoke,这样这声明了一个函数指针,然后进行赋值fp=Invoke就可以进行函数指针的调用了。声明函数指针时,只要函数返回值类型、参数个数、参数类型等保持一致,就可以声明一个函数指针了。注意,函数指针必须用括号括起来 void (*fp)(char* s)。

实际中,为了方便,通常用宏定义的方式来声明函数指针,实现程序如下:

typedef void (*FP)(char* s);
void Invoke(char* s);

int main(int argc,char* argv[])
{
FP fp;      //通常是用宏FP来声明一个函数指针fp
fp=Invoke;
fp("Hello World!\n");
return 0;
}

void Invoke(char* s)
{
printf(s);
}


函数指针数组

下面用程序对函数指针数组来个大致了解:

#include <iostream>
#include <string>
using namespace std;

typedef void (*FP)(char* s);
void f1(char* s){cout<<s;}
void f2(char* s){cout<<s;}
void f3(char* s){cout<<s;}

int main(int argc,char* argv[])
{
void* a[]={f1,f2,f3};   //定义了指针数组,这里a是一个普通指针
a[0]("Hello World!\n"); //编译错误,指针数组不能用下标的方式来调用函数

FP f[]={f1,f2,f3};      //定义一个函数指针的数组,这里的f是一个函数指针
f[0]("Hello World!\n"); //正确,函数指针的数组进行下标操作可以进行函数的间接调用

return 0;
}


回调函数

(1)概念:回调函数,顾名思义,就是使用者自己定义一个函数,使用者自己实现这个函数的程序内容,然后把这个函数作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。

(2)标准Hello World程序:

int main(int argc,char* argv[])
{
printf("Hello World!\n");
return 0;
}


将它修改成函数回调样式:

//定义回调函数
void PrintfText()
{
printf("Hello World!\n");
}

//定义实现回调函数的"调用函数"
void CallPrintfText(void (*callfuct)())
{
callfuct();
}

//在main函数中实现函数回调
int main(int argc,char* argv[])
{
CallPrintfText(PrintfText);
return 0;
}


修改成带参的回调样式:

//定义带参回调函数
void PrintfText(char* s)
{
printf(s);
}

//定义实现带参回调函数的"调用函数"
void CallPrintfText(void (*callfuct)(char*),char* s)
{
callfuct(s);
}

//在main函数中实现带参的函数回调
int main(int argc,char* argv[])
{
CallPrintfText(PrintfText,"Hello World!\n");
return 0;
}


至此,对回调函数应该有了一个大致的了解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: