1.函数指针
概念:指向函数的指针变量。在进行编译时,每个函数都会有一个入口地址,因此指向这个函数的指针便指向这个入口地址。
函数地址的用途:
(1).调用函数
(2).作为函数参数
函数指针的声明方式:例如 int (*func) (int a, int b);其中,(*func)中的括号是必须的,它会告诉编译器我们声明的是一个函数指针,不是一个指针函数。后面的形参列表要根据函数指针所指向的函数的形参来确定。函数指针调用函数的时,只需要通过func(x)或者(*func)(x)即可。
1数据类型 (指针变量名) 形参列表;
但是,上面的函数指针声明方式十分繁琐,因此可以使用typedef来简化。因此可以这个来声明:
1typedef int (*PF) (int x); 2PF pf;
于是,pf就是一个函数指针,在程序中的其他地方使用时会很方便。当使用函数指针来调用函数时,只需要通过pf(x)即可实现。函数指针也可可以指向被重载的函数,编译器会为我们自动区分这些重载的函数,从而解决函数重载的二义性调用问题。如下例所示:
1#include <iostream> 2 3using namespace std; 4 5// 函数指针声明 6// typedef void (*PFT) (char, char); 7typedef void (*PFT) (int, int); 8 9 10void bar(char ch, char i){ 11 cout << "bar: " << ch << " " << i << endl; 12} 13 14// 重载bar()函数 15void bar(int a, int b){ 16 cout << "重载bar()函数: " << a << " " << b << endl; 17} 18 19int main(){ 20 PFT pft = nullptr; 21 pft = bar; 22 pft('a', 65); 23 return 0; 24}
上述函数声明了一个pft的函数指针,同时重载了函数bar()。此时,如果不使用函数指针来调用函数bar('a',65)就会出现二义性。但是,如果使用函数指针pft进行调用就可以解决函数重载带来的二义性问题。因为函数指针pft指向的函数的形参类型是(int, int)。因此,当通过函数指针pft('a',65)调用bar()函数时,就会执行重载之后的bar()函数。
(1).调用函数
1#include <iostream> 2 3using namespace std; 4 5// 函数指针声明 6typedef void (*PFT) (char, int); 7 8 9void bar(char ch, int i){ 10 cout << "bar: " << ch << " " << i << endl; 11} 12 13 14 15int main(){ 16 PFT pft = nullptr; 17 pft = bar; 18 pft('a', 65); 19 return 0; 20}
上面的程序中,函数指针pft指向函数bar(),然后通过pft来实现输出字符和整数的功能。
(2).作为函数参数
函数指针的另一个作用是作为函数的参数,可以在一个函数的形参列表中传入
一个函数指针,然后就可以在这个函数的内部使用这个函数指针所指向的函数了,这种方式可以使程序更加清晰。
1#include <iostream> 2 3using namespace std; 4 5// 函数指针声明 6typedef void (*PFT) (char, int); 7 8 9void bar(char ch, int i){ 10 cout << "bar: " << ch << " " << i << endl; 11} 12 13 14void foo(char a, int b, PFT pf){ 15 pf(a, b); // 在foo()函数内部通过函数指针调用bar()函数 16} 17 18 19int main(){ 20 PFT pft = nullptr; 21 pft = bar; 22 foo('b', 888, pft); 23 return 0; 24}
2.函数对象
从一般的函数回调意义上来说,函数对象和函数指针是相同的。但是,函数对象却具有许多函数指针不具有的优点。函数对象可以使程序设计更加灵活,而且能够实现函数的内联调用,使整个程序实现性能加速。
函数对象:首先它是一个对象,而实际上只是这个对象具有函数的某些功能。因此,它才称之为函数对象。如果一个对象具有了某个函数的功能,它便可以称为函数对象。
如何使对象具有函数功能呢?答案是只需要为这个对象的操作符()进行运算符重载即可。如下例所示:
1#include <iostream> 2 3using namespace std; 4 5class A 6{ 7public: 8 int operator()(int x) 9 { 10 return x; 11 } 12}; 13 14int main() 15{ 16 A a; 17 int res = a(5); // 注意:此处不是调用构造函数,是使用()运算符重载实现的函数对象 18 cout << res << endl; 19 return 0; 20}
在上面的程序中,a就是一个函数对象。当执行a(5)时,实质上是利用了重载()运算符。因为函数对象既然是一个对象的话,就可以作为函数的形参,它完全可以代替函数指针。当我们想在形参列表中调用某个函数时,可以先声明一个具有这个函数功能的函数对象。然后,在函数的形参中使用这个对象,它的功能和函数指针作为函数形参的功能是一样的,而且更安全。
1#include <iostream> 2 3using namespace std; 4 5class A 6{ 7public: 8 int operator() (int a, int b){ 9 cout << a << '+' << b << '=' << a + b << endl; 10 return a; 11 } 12}; 13 14 15int addFunc(int a, int b, A& func){ 16 func(a, b); 17 return a; 18} 19 20 21 22int main() 23{ 24 A func; 25 addFunc(1, 4, func); 26 return 0; 27}
上面的程序中,首先定义了一个函数对象类A,并且重载了()运算符。目的是实现两个参数的加法并输出,然后在函数addFunc()中的形参列表里使用这个类对象func,从而实现两个数的加法。从泛型编程的角度来看,可以定义一个函数模板类,来实现更广泛类型的数据相加。如下例所示:
1#include <iostream> 2 3using namespace std; 4 5class A 6{ 7public: 8 template <typename T> 9 T operator() (T a, T b){ 10 cout << a << '+' << b << '=' << a + b << endl; 11 return a; 12 } 13}; 14 15template <typename T> 16T addFunc(T a, T b, A& func){ 17 func(a, b); 18 return a; 19} 20 21 22 23int main() 24{ 25 A func; 26 addFunc(1, 4, func); 27 addFunc(1.4, 3.8, func); 28 return 0; 29}
3.参考资料
[1] https://www.cnblogs.com/sgawscd/p/12228872.html
[2] https://www.cnblogs.com/lvpengms/archive/2011/02/21/1960078.html