函数指针
什么是函数指针
顾名思义,函数指针是指向函数的指针,和其他指针一样,函数指针指向某种特定类型。函数的类型由它的返回类型和形参类型共同决定。
函数指针的声明
要想声明一个可以指向函数的指针,只需要用指针替换函数名即可
//比较两个string对象的长度
bool lengthCompare(const string&, const string&);
//pf指向一个函数,该函数的两个参数是两个const string的引用,返回值是bool类型
bool(*pf)(const string&, const string&);//未初始化化
观察函数指针的声明,发现pf前面有个*,因此pf是指针;右侧是形参列表,表示pf指向的是函数;而左侧是函数的返回类型,这里是布尔型。最后得出pf是一个指向函数的指针。
注意
:*pf两端的括号必不可少。如果不写这对括号,则pf是一个返回值为bool类型的函数,即bool pf(const string&, const string&);pf是函数名
函数指针的使用
当我们把函数名作为一个值使用时,该函数自动转换成指针。
pf = lengthCompare; //pf指向名为lengthCompare的函数
pf = &lengthCompare; //等价的赋值语句:取地址符&是可选的
除此之外,函数指针也有其赋值规则
- 由于函数指针,本质上还是指针,所以我们可以设置指针的初值为nullptr或为0,表示该指针没有指向任何一个函数。
- 赋值的函数的返回类型必须与函数指针的返回类型一致
- 赋值的函数的形参列表必须与函数指针的形参列表完全匹配
//计算两个string对象的总长度
int sumLength(const string&, const string&);
//比较两个int对象的值
bool numCompare(int, int);
//比较指定长度字符串的大小
bool compareStr(const string&, const string&, int);
pf = nullptr; //正确:pf不指向任何函数
pf = sumLength; //错误:返回类型不匹配
pf = numCompare; //错误:形参列表不匹配
pf = compareStr; //错误:形参列表不匹配
我们还能直接使用指向函数的指针调用该函数,无须提前解引用指针。
bool b1 = pf("123", "1234"); //调用lengthCompare函数
bool b2 = (*pf)("123", "1234");//与上面等价,注意,括号不能少
bool b3 = lengthCompare("123", "1234");//另一个等价的调用
重载函数的指针
当我们程序中有重载函数时,如果定义了指向重载函数的指针,指针类型必须与重载函数中的某一个精确匹配,这样编译器才能通过指针类型决定选用哪个函数。
void ff(int);
void ff(int*);
void(*pf1)(int) = ff; //正确:pf1指向ff(int)
void(*pf2)(short) = ff; //错误:没有与指针类型匹配的重载函数ff
bool(*pf3)(int) = ff; //错误:pf3和ff的返回类型不一致
函数指针作为形参
和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。此外,形参看起来是函数类型,实际上是当成指针使用。
//比较两个string对象的长度
bool lengthCompare(const string&str1, const string&str2){
return str1.size() >= str2.size();
}
//pf指向一个函数,该函数的两个参数是两个const string的引用,返回值是bool类型
bool(*pf)(const string&, const string&);//未初始化化
//第三个形参是函数类型,它会自动转换成指向函数的指针
void useBigger(const string&str1, const string&str2, bool pf(const string&, const string&)){
if (pf(str1, str2)) {
cout << "str1:" << str1 << endl;;
}
else {
cout << "str2:" << str2 << endl;;
}
}
int main()
{
pf = lengthCompare;
string str1 = "12345", str2 = "123456";
useBigger(str1, str2, pf);//输出str2:123456
useBigger(str1, str2, lengthCompare);//和上面等价
return 0;
}
对于函数useBigger的第三个形参,bool(*pf)(const string&, const string&)
和bool pf(const string&, const string&)
是等价的,即我们可以直接把函数作为实参使用,它会自动转换为函数指针。
简化代码
直接使用函数指针类型显得冗长而烦琐。类型别名和decltype能让我们简化使用函数指针的代码
//Func1、Func2、Func3、Func4是函数类型,下面四种写法都是等价的
typedef bool Func1(const string&, const string&);
typedef decltype(lengthCompare)Func2;
using Func3 = bool(const string&, const string&);
using Func4 = decltype(lengthCompare);
//FuncP1、FuncP2、FuncP3、FuncP4是指向函数的指针,下面四种写法都是等价的
typedef bool(*FuncP1)(const string&, const string&);
typedef decltype(lengthCompare)*FuncP2;
using FuncP3 = bool(*)(const string&, const string&);
using FuncP4 = decltype(lengthCompare)*;
注意
:decltype(函数名)返回的是函数类型,此时不会将函数类型自动转换成指针类型,所以需要在返回结果上显示地加上*才能得到指针类型。
通过给函数或者函数指针起别名,我们可以使用以下更简单的方式声明之前的useBigger函数
//下面两者等价
void useBigger(const string&, const string&, Func1);
void useBigger(const string&, const string&, FuncP1);
上面两个声明语句声明的都是同一个函数,只不过在第一条语句中,编译器自动将Func1表示的函数类型转换成指针
函数指针作为返回类型
和数组类似,虽然不能返回一个函数,但是能返回指向函数的指针。要想声明一个返回函数指针的函数,最简单的方法是使用类型别名,方法同上。
注意
:和函数指针作为形参不同,函数指针作为返回类型,编译器不会自动将函数返回类型转换成指针进行处理。
using F = int(int*, int); //F是函数类型,不是指针
using PF = int(*)(int*, int); //PF是指针类型
PF f1(int); //正确:PF是指向函数的指针,f1返回指向函数的指针
F f1(int); //错误:F是函数类型,不允许使用返回函数的函数
F* f1(int); //正确:显式地指定返回类型是指向函数的指针
如果不适用类型别名,则可以写成int(*f1(int))(int*, int);
,按照由内向外的顺序阅读:我们看到f1有形参列表,所以f1是个函数;f1前面是*,所以f1返回一个指针;进一步观察,指针的类型本身也包含形参列表,因此f1是返回函数指针的函数。
函数指针和指针函数的区别
指针函数
:指针函数是返回类型为指针的函数,即本质是一个函数。
声明格式:类型名* 函数名(参数表)
在调用指针函数时,需要一个同类型的指针来接收其函数的返回值
函数指针
:函数指针是指向函数地址的指针变量,本质是一个指针,函数也是有分配内存的,所以指向函数地址的指针,即为函数指针。
声明格式:类型名 (*指针名)(参数表)
简单地说:指针函数是函数,返回值是指针,函数指针是指针,指向的是函数
参考
《C++ Primer(第五版)》