指针是C++中的一种复合类型,是“指向(pointer to)”另外一种类型的复合类型,实现了对其他对象的间接访问。
函数指针指向的不是对象,而是函数。它基于函数类型的定义。
回想函数的三要素:返回类型、函数名、形参类型,函数类型指的就是由返回类型和形参类型共同决定的。
bool shorter(const string &, const string &); // 函数的类型是 // bool(const string &, const string &) // 是这样的一类函数: // 返回值是bool类型,需要两个const string&类型的函数此时,可以将函数名位置用指针替换,就声明了指向上述函数类型的指针:
bool (*pf)(const string &, const string &); // 未初始化解析上面的声明,依旧是按照从内向外、先右后左的口诀。
很多读者看到函数指针的声明和定义方法就望而却步了,但其实记住从内向外、先右后左以及学会类型处理,理解函数指针很简单。
关于函数指针,我们需要了解:
函数指针的声明&定义函数指针的使用类型处理(typedef、using、auto、decltype)注意:(*pf)的括号必不可少。
上述代码在初始化pf时,并没有在等号=右侧使用&,原因是①把函数名作为一个值使用时,函数自动转换成指针(除了decltype)。
定义了一个有明确指向的函数指针后,可直接通过调用运算符()调用该函数:
pf("hello", "goodbye");可以不解引用指针,也可以解引用,但是必须加括号:
(*pf)("hello", "goodbye");其实,带有明确指向的函数指针属于可调用对象(callable object)。
我们可以声明一个具有函数指针形参的函数:
void callCompare(bool pf(const string &, const string &), int count); // 第一个参数是函数类型为 // bool(const string &, const string &) // 的函数指针注意:此处虽然看起来是函数类型的声明,但实际上②函数类型作为形参会自动的转换为相应类型的函数指针。当然写成指针类型也是可以的。
void callCompare(bool (*pf)(const string &, const string &), int count); // 和上面等价,显式添加了*在使用callCompare时:
callCompare(shorter, 1);这里也没有用取地址运算符&,和1.1叙述的原因一样。
假如我想定义一个返回与shorter同类型函数指针的函数,该如何声明呢?
从里向外,先右后左。
首先确定我想定义的函数的三要素:
函数名,假设retFunc参数列表,假设接受一个int返回值,一个与shorter同类型函数指针bool (*)(const string &, const string &)自然而然想到可以这么写:
// ERROR,这是错误的 bool (*)(const string &, const string &) retFunc(int);这么写一定是不行的,因为左边一大坨不符合编译器编译的原理,从retFunc向左看到了参数列表,完全不知道是什么。
因此必须把指针*放在retFunc的左边。
bool (*retFunc(int))(const string &, const string &);①从标识符开始,向右看到了参数列表,因此retFunc是一个函数。
②右括号,不能向右了,向左,看到了指针*,因此返回的是一个指针。
③看到左括号,向外,继续向右,看到参数列表,因此返回的是函数指针。
④再向左,看到了bool,可以确定了返回的函数指针的类型。
若每次都像上面一样使用这么复杂的类型定义,程序一定很乱。
这时,typedef、using、auto、decltype就派上了用场。
我们可以用typedef、using给函数类型声明一个简单的名字;在明确知道使用的是哪个函数时,可以使用auto和decltype。
例如:
// 声明了一个函数类型 using FuncType = int(int &, int); // typedef int FuncType(int &, int); // using PFuncType = int (*)(int &, int); typedef int (*PFuncType)(int &, int); // 下面的函数就是上面的类型 int add_to(int &des, int ori) { return des += ori; } int minus_to(int &des, int ori) { return des -= ori; } int multiply_to(int &des, int ori) { return des *= ori; } int divide_to(int &res, int ori) { return res /= ori; } int main() { FuncType *pf0 = add_to; auto *pf1 = minus_to; decltype(multiply_to) *pf2 = multiply_to; PFuncType pf3 = divide_to; int a = 4; // 通过函数指针调用 pf0(a, 2); // 6 pf1(a, 3); // 3 pf2(a, 4); // 12 pf3(a, 3); // 4 }这样的声明就简单多了。
注意:decltype和auto不会自动的获得函数指针类型,都需要添加*。
函数类型是根据函数定义的返回值和形参列表抽象出来的一类函数。
可以使用函数类型定义指向同种类型的函数指针。
使用函数指针可以间接调用函数,且可以实现函数指针作为形参、函数指针作为返回值等操作。
在使用函数指针时,使用类型别名(typedef、using)和类型推断(auto、decltype),可以使代码更清晰。