1,语法
2,应用场景
3,应用技巧
4,回调函数
参考《C++ Primer Plus》《Effective C++》和“博客:类的成员函数指针”
一、函数指针的一般原理
与数据项类似,函数也有地址。函数的地址是存储其机器语言代码的内存的起始地址。函数调用,实际上就是执行内存在以函数地址为起始点的这段机器码,故只要获取函数地址,即可执行函数调用。而函数指针就是这样一中指向某类特定函数的
的内存的起始地址的指针类型。可以编写将另一个函数的地址作为参数的函数,它运行在不同的时间传递不同的函数的地址,这意味着可以在不同的时间使用不同的函数,它在一定程度上可以替代“继承多态”的功能,参考
《Effective C++》item35
。
与类的非静态数据成员不同,类的成员函数不会在运行时为每个类的实例对象生成一份专有的
机器语言代码,
类的成员函数在内存中只有一份,即类的成员函数的地址也是唯一的。这也就解释了,取类的成员函数的地址时,只需要将类作为限定,而不需要知道类的实例对象。从内存角度看,
类的成员函数和一般函数是一样的,特别是类的静态成员函数。但是,类的成员函数为了防止与其他类的成员函数名称冲突,编译器会添加一些额外的符号进行区分,故需要由类名进行引导搜素。
二、一般函数指针的用法
一般函数指针指的是除“
类的非静态成员函数指针
”之外的函数指针,它没有类的限定,只需要匹配函数签名(signature)和返回值即可。参考“ 博客:一般函数指针和类的成员函数指针
”。在使用上有两种简化方式,一种是使用auto,另一种是使用typedef。如例:
已知函数:
int Func(int a, int b);
可定义函数指针(方式一):
auto pFunc = &Func;
方式二:
typedef int(*
pFunc
)(int, int);
pFunc pfFunc = &Func;
第二种方式适合反复使用和作为参数。
三、类的非静态成员函数指针的用法
类的静态成员函数与一般函数类似,其函数指针用法参考上一条,故以下将类的非静态成员函数简称类的成员函数。而类的成员函数指针在使用的时候,需要使用类名进行限定。
参考“ 博客:一般函数指针和类的成员函数指针
”:
class CA;
typedef int(CA::*pClassFunc)(int ,int);
注意域运算符和指针标示符的顺序。此外,一般可以将该typedef放在类的构造函数附近进行声明,限定其作用域。
int Resule(CA* pA, pClassFunc pfFunc, int a, int b)
{
return (pA->*pClassFunc)(a, b);
}
注意函数调用符和解引用符的顺序。
四、函数指针应用
1,增加接口的弹性,提高代码复用。示例(来自于
《C++ Primer Plus》)
假设要设计一个名为estimate()的函数,估算编写指定行数的代码所需的时间,并且希望不同的程序员都将使用该函数。对于所有用户来说,estimate()中的一部分代码都是相同的 ,但该函数运行每个程序员提供自己的算法来估算时间。为实现这种目标,采用的机制是,将程序要使用的算法函数的地址传递给esimate()。
// fun_ptr.cpp -- pointer to functions
#include
double betsy(int);
double pam(int);
void estimate(int lines, double (*pf)(int));
int main()
{
using namespace std;
int code;
cout << "How many lines of code you need ? ";
cin >> code;
cout << "Here's Betsy's estimate: \n";
estimate(code,betsy);
cout << "Here's Pam's estimate: \n";
estimate(code, pam);
cin.get();
return 0;
}
double betsy(int lns)
{
return 0.05 *lns;
}
double pam(int lns)
{
return 0.03 * lns + 0.0004 * lns * lns;
}
void estimate(int lines, double (*pf)(int))
{
using namespace std;
cout << lines << " lines will take ";
cout << (*pf)(lines) << " hour(s)\n";
}
附:通过这种设计,可以复用estimate()函数中除算法外的其他部分。如果将estimate作为接口,它将具有较强的弹性,用户可以根据自己的需要自由选择需要的函数传递给它。特别的,用户在不修改该接口(estimate())的情况下,可以自己实现一个算法函数,传递给该接口,实现不同个的计算效果。
STL的算法函数,传递给functor参数的设计方式也是基于这种设计模式。如自定义排序方式,参考
《C++ Primer Plus》P681。
2,替代虚函数机制实现多态,
参考
《Effective C++》item35
。它实际上也是Strategy设计模式的一种应用。(策略与机制相分离)示例如下:
计算游戏任务的健康指数
class GameCharacter; // 前置声明
// 以下函数是计算健康指数的缺省算法
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter
{
public:
typedef int (*HealthCalcFunc)(
const GameCharacter&
);
explict
GameCharacter(HealthCalcFunc hcf =
defaultHealthCalc)
:healthFunc(hcf)
{}
int healthValue() const { return
healthFunc(*this); }
...
private:
HealthCalcFunc
healthFunc; // 类成员是一个函数指针
}
五,函数指针用于回调
参考Qt中的“
qInstallMessageHandler”的用法