声明函数指针并实现回调
副标题#e#
措施员经常需要实现回调。本文将接头函数指针的根基原则并说明如何利用函数指针实现回调。留意这里针对的是普通的函数,不包罗完全依赖于差异语法和语义法则的类成员函数(类成员指针将在另文中接头)。
声明函数指针
回调函数是一个措施员不能显式挪用的函数;通过将回调函数的地点传给挪用者从而实现挪用。要实现回调,必需首先界说函数指针。尽量界说的语法有点不行思议,但假如你熟悉函数声明的一般要领,便会发明函数指针的声明与函数声明很是雷同。请看下面的例子:
void f();// 函数原型
上面的语句声明白一个函数,没有输入参数并返回void。那么函数指针的声明要领如下:
void (*) ();
让我们来阐明一下,左边圆括弧中的星号是函数指针声明的要害。别的两个元素是函数的返回范例(void)和由边圆括弧中的进口参数(本例中参数是空)。留意本例中还没有建设指针变量-只是声明白变量范例。今朝可以用这个变量范例来建设范例界说名及用sizeof表达式得到函数指针的巨细:
// 得到函数指针的巨细
unsigned psize = sizeof (void (*) ());
// 为函数指针声明范例界说
typedef void (*pfv) ();
pfv是一个函数指针,它指向的函数没有输入参数,返回类行为void。利用这个范例界说名可以埋没巨大的函数指针语法。
指针变量应该有一个变量名:
void (*p) (); //p是指向某函数的指针
p是指向某函数的指针,该函数无输入参数,返回值的范例为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋值,值的内容是署名匹配的函数名和返回范例。譬喻:
void func()
{
/* do something */
}
p = func;
p的赋值可以差异,但必然要是函数的地点,而且署名和返回范例沟通。
通报回调函数的地点给挪用者
此刻可以将p通报给另一个函数(挪用者)- caller(),它将挪用p指向的函数,而此函数名是未知的:
void caller(void(*ptr)())
{
ptr(); /* 挪用ptr指向的函数 */
}
void func();
int main()
{
p = func;
caller(p); /* 通报函数地点到挪用者 */
}
假如赋了差异的值给p(差异函数地点),那么挪用者将挪用差异地点的函数。赋值可以产生在运行时,这样使你能实现动态绑定。
#p#副标题#e#
挪用类型
到今朝为止,我们只接头了函数指针及回调而没有去留意ANSI C/C++的编译器类型。很多编译器有几种挪用类型。如在Visual C++中,可以在函数范例前加_cdecl,_stdcall可能_pascal来暗示其挪用类型(默认为_cdecl)。C++ Builder也支持_fastcall挪用类型。挪用类型影响编译器发生的给定函数名,参数通报的顺序(从右到左或从左到右),仓库清理责任(挪用者可能被挪用者)以及参数通报机制(仓库,CPU寄存器等)。
将挪用类型当作是函数范例的一部门是很重要的;不能用不兼容的挪用类型将地点赋值给函数指针。譬喻:
// 被挪用函数是以int为参数,以int为返回值
__stdcall int callee(int);
// 挪用函数以函数指针为参数
void caller( __cdecl int(*ptr)(int));
// 在p中诡计存储被挪用函数地点的犯科操纵
__cdecl int(*p)(int) = callee; // 堕落
指针p和callee()的范例不兼容,因为它们有差异的挪用类型。因此不能将被挪用者的地点赋值给指针p,尽量两者有沟通的返回值和参数列。