成员函数指针与回调函数
首先来提一个问题,函数能作为参数吗?也许你会疑惑,但它确实可以,Windows的回调函数就是这么一个东西,就是那个callback,当满足一定条件的时候就会调用这个回调函数,那怎么知道什么时候才满足这个条件呢?这个不用我们操心,系统会帮我们高效的检查,这个检查就是监听器的工作?有很多名词没听过?没关系,今天我讨论的主题不是这个。听过那最好啦!那么设置一个回调函数的时候,是不是有点烦,参数类型啊,参数个数啊,返回类型啊一定要一致,还要加一个callback声明,否则就会调用失败!!!切记!!!
听起来,callback函数有点玄,当你条件满足的时候才去调用,所以很高效,那它是怎么实现的呢?考虑下面的代码:
typedef void (*ptrFn)(char);
ptrFn pFn;
char keyDown = 0;
void Function(charkey)
{
switch(key)
{
case 'd':cout<<"Dwas pressed!Move Right!"<<endl;break;
case 's':cout<<"Swas pressed!Move Down!"<<endl;break;
case 'w':cout<<"Wwas Pressed!Move Up!"<<endl;break;
case 'a':cout<<"Awas Pressed!Move Left!"<<endl;break;
default:break;
}
}
void SetCallBack()
{
keyDown = getch();
while(keyDown!= '0')
{
pFn(keyDown);
keyDown = getch();
}
}
void main()
{
pFn = Function;
SetCallBack();
}
上面的代码不难理解,就是按下w,a,s,d来控制人物的移动,因为用的getch()函数读取一个字符(按下一个字符后,不需要按回车),要记得加入#include <conio.h>,上面的几行代码有点回调函数的味道了,只是windows的回调函数比这个复杂多了,咱们先不讨论那个!
先来理解下上面的代码,typedef void (*ptrFn)(char);typedef重命名函数指针,ptrFn指向的是一个空返回类型,参数为 char 的函数,然后我们要设置自己的回调函数的时候就可以这样写:
void 函数名 (char 参数名)
{
//要执行的代码
}
然后在main()主函数里,将pFn = 函数名,这样就可以调用你设置的函数了!别看了,自己先去调试一下,理解好了再往下看!不然你又会迷迷糊糊的了!如果已经理解的同学,继续前进!
那么我想将上面的回调函数放在类里面,作为成员函数来调用行不行呢?你马上会想到以下的代码:
class Game
{
private:
floatx,y;
public:
voidFunction(char key);
};
void Game::Function(charkey)
{
switch(key)
{
case 'd':cout<<"Dwas pressed!Move Right!"<<endl;break;
case 's':cout<<"Swas pressed!Move Down!"<<endl;break;
case 'w':cout<<"Wwas Pressed!Move Up!"<<endl;break;
case 'a':cout<<"Awas Pressed!Move Left!"<<endl;break;
default:break;
}
}
void main()
{
Game game;
pFn = game.Function;
SetCallBack();
}
你会发现无法编译通过,说什么:无法从“void (__thiscall Game::* )(char)”转换为“ptrFn”!!!
这是为什么?理解一下错误信息,无法从(Gme::*)(char)转换为ptrFn,也就是说game.Function是Game::*类型的,而ptrFn必须指向的是void返回类型的,因为Function是classGame里面的东西,所以多了一个域的作用,而我们typedef void(*ptrFn)(char);的时候指明的是没有域作用的,也就是全局的,也就说“域”不匹配,当然无法编译通过!!!
那怎么办,既然“域”不匹配,那么我们就将它匹配行啦。那么指明匹配?typedef void (*ptrFn)(char);将这个改成:
typedef void (Game::*ptrFn)(char);这样?
我们发现这样还是不行,会出现一大堆的错误,就算这样能行,那么我们就一定得实例化一个对象,才能正常使用,而回调函数是不能通过实例化再来调用的。它必须得在程序运行的时候就存在了,而且我们也不可能改成这样typedef void (Game::*ptrFn)(char);如果这个是windows写的,那你怎么改?所以我们得另寻它法。
匹配typedef的不行,那么只能匹配Game里面的了,那么怎么匹配,因为tepedef需要指向的全局的,那么怎么Game::Function改成全局的?其实不难,将它声明为静态的而且是public的就行了,这样的话,只要在使用Function的时候加个域说明Game::就行了,所以就有了下面的代码:
class Game
{
private:
floatx,y;
public:
static void Function(charkey);
};
void Game::Function(charkey)
{
switch(key)
{
case 'd':cout<<"Dwas pressed!Move Right!"<<endl;break;
case 's':cout<<"Swas pressed!Move Down!"<<endl;break;
case 'w':cout<<"Wwas Pressed!Move Up!"<<endl;break;
case 'a':cout<<"Awas Pressed!Move Left!"<<endl;break;
default:break;
}
}
void main()
{
pFn = Game::Function;
SetCallBack();
}
跟之前的代码没什么区别,只是在Game里的Function()加了一个static,
main()主函数里面加了一个域说明pFn = Game::Function;因为Game::Function()是静态的,所以不需要实例化一个对象,因为它程序一运行的时候已经被创建了。