C++基础入门教程(九):函数指针之回调
在Java,要实现某个时间调用某段代码,是很简单的事情,那就是使用接口。
而在C++里,有一个比较高深的方式,那就是使用函数指针。
比如Cocos2d-x的定时器(schedule)、消息订阅(NotificationCenter)都使用了函数指针来完成回调的功能。
这也是为什么我们总是能把某个函数作为参数传进去,然后在某个时刻这个函数会被调用。
一、函数的地址
要获取一个int变量的地址很简单,比如intnum;那么num的地址就是&num。
而获取函数的地址更简单,函数的名字就是函数的地址,如下代码:
voidhello(); int_tmain(intargc,_TCHAR*argv[]) { autop=hello; p(); return0; } voidhello() { cout<<"helloworld"; }
我们定义了一个hello函数,然后直接把函数名字赋值给指针p,于是,就可以把p当成了hello函数来使用了。
这很简单吧。
二、声明函数指针
获取函数的地址很简单,但是,如何声明函数指针就变得不那么简单了。
我们总不能每次都使用auto来逃避吧?有时候我们不得不显式地声明函数指针,那么,如何声明呢?
还记得我们说过的typedef定义类型别名吗?函数指针的声明也是一样的规则,先声明一个函数,如:voidhello();
然后把函数名字换成指针,如:void(*p)();
没错,就是这么简单,void(*p)();就是voidhello();的声明了。
立刻再来试试,这个函数:intgetValue(floatdt);
它的函数指针声明是什么?没错,就是:int(*p)getValue(floatdt);
没错,就是这么简单intgetValue(floatdt);就是int(*p)getValue(floatdt);的函数指针声明了。
立刻再来试试,这..(小若:停~!别以为我不在你就可以乱来!)
好吧,那就不继续试了,我们来看看,刚刚那段代码可以这样写:
voidhello(); int_tmain(intargc,_TCHAR*argv[]) { void(*p)(); p=hello; p(); (*p)();//偷偷加了这句 return0; } voidhello() { cout<<"helloworld"; }
好了,很简单,不多说了~
另外,有没有发现我偷偷又加了一句代码?
没错,用(*p)();的方式也通过能成功调用hello函数,这是为什么呢?
三、历史原因
由于p是指针,它指向的是hello函数的地址,所以,*p就代表hello函数,于是,(*p)()就等于hello(),这是正常的逻辑。
所以,其实(*p)()才是比较正常的调用方式。
然而,由于函数名本来就是指向了函数的指针,也就是说,hello其实也是指向了函数的地址。
换句话说,p和hello其实都是指针,那么,p的调用方式和hello的调用方式应该也是一样的,于是,p()就相当于hello()。
这两种方式都是正确的,其实语法这东西,就是人定的,历史上前辈对这两种方式各持所见,于是就容忍了这两种看似冲突的方式同时存在了。
不过,我想,大部分人都会更喜欢直接用p(),而不是(*p)()吧。
四、typedef挽救复杂的函数指针
如下代码:
stringhehe1(intnum,floatvalue); stringhehe2(intnum,floatvalue); stringhehe3(intnum,floatvalue);
int_tmain(intargc,_TCHAR*argv[]) { /*声明函数指针数组*/ string(*p[3])(intnum,floatvalue)={hehe1,hehe2,hehe3};
stringresult=p[1](1,2); cout<<result.c_str()<<endl; return0; }
stringhehe1(intnum,floatvalue) { return"haha1"; } stringhehe2(intnum,floatvalue) { return"haha2"; } stringhehe3(intnum,floatvalue) { return"haha3"; }