在注册一个回调函数的时候,我们常常使用函数指针。c++/c的连接器在连接程序的时候必须把函数体的首地址绑定到对该函数调用语句上,因此函数地址必须在编译时就确定下来,也就是编译器为函数体生成代码的时候。这样的话,函数地址就是一个编译时常量。函数指针就是指向函数体的指针,其值就是函数体的首地址。而在源代码层面,函数名就代表函数的首地址,所以把函数名直接指派给一个同类型的函数指针而不需要使用“&”运算符。
typedef int (* FunPtr)(const char *);
FunPtr fp1 = puts;double (* fp3)(double) = sqrt;通过函数指针来调用函数有两种方式:
(1)直接把函数指针变量当做函数名,然后填入参数:int len = fp1("ggg");
(2)将函数指针的反引用作为函数名,然后填入参数:double d = (*fp3)(100);
函数指针数组:
#include#include using namespace std;double (* fp[2])(double) = {sqrt, cos};int main(void){ for(int i=0; i<2; i++) { cout << fp[i](100) << endl; } return 0;}
函数连接的本质是把函数地址绑定到函数的调用语句上(实际上就是一个跳转指令),并且一般的函数调用语句可以在编译时就完成这个绑定(静态连接)。然而由于数组下表i是一个变量,它在编译时是没有值的,因此,fp[i]在编译时也就没有确定的值,即fp[i](100)在编译时无法绑定到一个确定的函数体上。这正是函数指针数组不同于一般函数调用的地方。于是,连接器只能把这个通过函数指针的调用推迟到运行时再绑定(运行时连接)。
类成员函数的指针:
#includeusing namespace std;class Test{public: void f(void){ cout << "f" << endl; } static void g(void){ cout << "g" << endl; } virtual void h(void){ cout << "h" << endl; }};int main(void){ typedef void (* GFPtr)(void); GFPtr fp = Test::g; //取静态成员函数的地址的方法和取一个全局函数的地址相似 fp(); typedef void (Test::*MemFuncPtr)(void); MemFuncPtr m1 = &Test::f; //或者MemFuncPtr m1 = Test::f; MemFuncPtr m2 = &Test::h; Test test; //使用对象和成员函数指针调用成员函数 (test.*m1)(); (test.*m2)(); Test *pTest = &test; (pTest->*m1)(); //使用对象指针和成员函数指针调用成员函数 (pTest->*m2)(); return 0;}