C++ 函数指针

C++ 函数指针

一、指针

  • 在声明语句中指针的类型实际上被用于指定它所指向对象的类型,指针的类型都要和它所指向的对象严格匹配。
int iVal = 12;
//P存放变量iVal的地址,iVal的类型为int
int *p = &iVal;

二、函数指针

  • 和其他指针一样,函数指针指向某种特定类型。
  • 函数的类型由它的返回类型和形参类型共同决定,与函数名无关。
  • 要想声明一个可以指向该函数的指针,只需要用指针替换函数名即可。
//函数
bool lengthCompare(const string&, const string&);
//pf为指针,指向一个函数,函数的类型为:bool (const string&, const string&)
bool (*pf)(const string&, const string&);
//函数,返回值为:bool*。*pf两端的括号必不可少。如果不写这对括号,则pf是一个返回值为bool指针的函数
bool *pf(const string&, const string&);

如何理解一个函数指针?

bool (*pf)(const string&, const string&);

从我们声明的名字开始观察,pf前面有个*,因此pf是指针;右侧是形参列表,表示pf指向的是函数;再观察左侧,发现函数的返回类型是布尔值。因此,pf就是一个指向函数的指针,其中该函数的参数是两个const string的引用,返回值是bool类型。

三、使用函数指针

  • 当我们把函数名作为一个值使用时,该函数自动地转换成指针。
//函数
bool lengthCompare(const string&, const string&);
//pf为指针,指向一个函数,函数的类型为:bool (const string&, const string&)
bool (*pf)(const string&, const string&);
//函数指针 指向 lengthCompare
pf = lengthCompare;
//等价 取地址符&是可选的
pf = &lengthCompare;
  • 我们还能直接使用指向函数的指针调用该函数,无须提前解引用指针。
//使用指向函数的指针,无须提前解引用指针
bool b1 = pf("hello", "goobye");
//等价 使用指向函数的指针,提前解引用指针
bool b2 = (*pf)("hello", "goobye");
//等价 使用函数名
bool b3 = lengthCompare("hello", "goobye");
  • 我们可以为函数指针赋一个nullptr或者值为0的整型常量表达式,表示该指针没有指向任何一个函数。
  • 函数指针的类型都要和它所指向的函数类型严格匹配。
/*以下三个函数,函数类型都不一样*/
string::size_type sumLength(const string&, const string&);
bool cstringCompare(const char*, const char*);
bool lengthCompare(const string&, const string&);

//pf为指针,指向一个函数,函数的类型为:bool (const string&, const string&)
bool (*pf)(const string&, const string&);

//正确 该指针没有指向任何一个函数
pf = 0;
//正确 函数和指针类型严格匹配
pf = lengthCompare;
//错误 形参类型不匹配
pf = cstringCompare;
//错误 返回值类型不匹配
pf = sumLength;

四、重载函数的指针

  • 编译器通过指针类型决定选用哪个函数,指针类型必须与重载函数中的某一个精确匹配。
//重载函数
void fun(int*);
void fun(unsigned int);

//正确 匹配void fun(unsigned int);
void (*pf1)(unsigned int) = fun;
//错误 形参类型不匹配
void (*pf2)(int) = fun;
//错误 返回值类型不匹配
double (*pf3)(int*) = fun;

五、函数指针形参

  • 虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。
//函数类型的形参,自动转换成指向函数的指针
void useBigger(const string &s1, const string&s2, bool pf(const string&, const string&));
//显式将形参定义为指向函数的指针
void useBigger(const string &s1, const string&s2, bool (*pf)(const string&, const string&));
  • 我们可以直接把函数作为实参使用,此时它会自动转换成指针。
//函数类型的实参(lengthCompare),自动转换成指向函数的指针
useBigger(s1, s2, lengthCompare);
  • 直接使用函数指针类型显得冗长而烦琐。类型别名和decltype能让我们简化使用了函数指针的代码。
  • 我们使用typedef定义自己的类型。
  • decltype返回函数类型,此时不会将函数类型自动转换成指针类型。因为decltype的结果是函数类型,所以只有在结果前面加上*才能得到指针。
//函数类型别名
typedef bool Func(const string&, const string&);
//等价  decltype获取函数lengthCompare的类型
typedef decltype(lengthCompare) Func2;

//函数指针别名
typedef bool (*FuncP)(const string&, const string&);
//等价 decltype获取函数lengthCompare的类型,加上*才能得到指针
typedef decltype(lengthCompare) *FuncP2;
  • 编译器自动地将Func表示的函数类型转换成指针。
//函数类型的实参(Func),自动转换成指向函数的指针
void useBigger(const string &s1, const string&s2, Func);
//实参为函数指针
void useBigger(const string &s1, const string&s2, FuncP2);

六、返回指向函数的指针

  • 虽然不能返回一个函数,但是能返回指向函数类型的指针。
  • 必须把返回类型写成指针形式,编译器不会自动地将函数返回类型当成对应的指针类型处理。

声明一个返回函数指针的函数的3种方法:

1.使用类型别名

  • 要想声明一个返回函数指针的函数,最简单的办法是使用类型别名。
  • 和函数类型的形参不一样,返回类型不会自动地转换成指针,我们必须显式地将返回类型指定为指针。
//函数类型 别名
using F = int(int*, int);
//函数指针 别名
using PF = int(*)(int*, int);

/*以下,f1返回*/
//正确 PF是指向函数的指针
PF f1(int);
//错误 不能返回函数类型
F f1(int);
//正确 显式地将返回类型指定为指向函数的指针
F *f1(int);

2.直接声明

  • 按照由内向外的顺序阅读这条声明语句:我们看到f1有形参列表,所以f1是个函数;f1前面有*,所以f1返回一个指针;进一步观察发现,指针的类型本身也包含形参列表,因此指针指向函数,该函数的返回类型是int。
int(*f1(int))(int*, int);

3.使用尾置返回类型

  • 任何函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效。
  • 尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto。
auto f1(int) -> int(*)(int*, int)

七、将decltype用于函数指针类型

五、函数指针形参 中已经使用了类型别名和decltype简化使用了函数指针的代码,用在函数指针形参,以下例子用在返回指向函数的指针。

  • 如果我们明确知道返回的函数是哪一个,就能使用decltype简化书写函数指针返回类型的过程。
  • 当我们将decltype作用于某个函数时,它返回函数类型而非指针类型。因此,我们显式地加上*以表明我们需要返回指针,而非函数本身。
//函数 两个函数类型一样
string::size_type sumLength(const string&, const string&);
string::size_type largerLenth(const string&, const string&);
//decltype获取函数sumLength的类型,显式地加上*以表明我们需要返回指针
decltype(sumLength) *getFun(comst string&);

八、练习题

练习1

编写函数的声明,令其接受两个int形参并返回类型也是int;然后声明一个vector对象,令其元素是指向该函数的指针。

解:

int func(int, int);
//decltype获取func类型,显式地加上*以表明我们是指向该函数的指针
std::vector<decltype(func)*> vec;

练习2

编写4个函数,分别对两个int值执行加、减、乘、除运算;在上一题创建的vector对象中保存指向这些函数的指针。

解:

/*以下函数类型相同  都为:int (int, int),即decltype(func) */
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }

//函数作为实参使用,会自动转换成指针
vec.push_back(add);
vec.push_back(subtract);
vec.push_back(multiply);
vec.push_back(divide);

练习3

调用上述vector对象中的每个元素并输出结果。

解:

std::vector<decltype(func) *> vec{ add, subtract, multiply, divide };
//变量vector中的函数指针,直接使用指向函数的指针调用函数,无须提前解引用指针
for (auto f : vec)
          std::cout << f(2, 2) << std::endl;

PS:本文基于C++ Primer(中文版)(第5版) 6.7 函数指针 整理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值