一文搞懂函数指针

函数指针

什么是函数指针

顾名思义,函数指针是指向函数的指针,和其他指针一样,函数指针指向某种特定类型。函数的类型由它的返回类型形参类型共同决定。

函数指针的声明

要想声明一个可以指向函数的指针,只需要用指针替换函数名即可

//比较两个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(第五版)》

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倒地不起的土豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值