首页 > 代码库 > C++中的函数指针

C++中的函数指针

时间:2014.06.14

地点:基地

-------------------------------------------------------------------------------

一、函数指针简介

  函数指针指向的是一个函数,而不是一个对象。但函数指针也和其他普通指针一样,指向特定的函数类型,函数的类型由返回类型和形参类型共同决定,与函数名无关,就像和变量名无关一样。比如:

bool LengthCompare(const string&,const string& );
上面这个函数的类型即 bool (const string&,const string&),要想声明一个指向这样一个函数类型的指针可以这样:

bool (*pf)(const string&,const string&);  //函数指针未初始化
即用一个指针变量名加*替换掉函数名即可。这和声明普通指针和类型,声明普通指针时我们也是用一个指针变量名加*放在类型声明后。在这里,我们看到pf前面有*,说明pf是一个指针变量名,右侧有形参列表,表pf指向的是一个函数,再看左侧的返回类型,表明pf函数指针指向的函数返回一个布尔值。这里声明一个函数指针时一定记得将 *pf括起来,否则比如这样:

bool* pf(const string&,const string&);
这将声明的是一个普通函数,它返回一个bool* 

-------------------------------------------------------------------------------

二、函数指针的使用

函数名就像数组名,当我们将函数名最为一个值使用时,该函数会自动转换成指针,可直接赋给函数指针。比如下面是等价的:

pf=LengthCompare;  //pf指针指向函数LengthCompare
pf=&LengthCompare;  //也是一样的,所以取址符在这里是可选的
同时,我们还可以使用指向函数的指针直接调用所指向的函数,无需解引用指针,当然解引用也没错,下面三种调用方式等价:

bool b1=pf("hello","world");
bool b2=(*pf)("hello","world");
bool b3=LngthCompare("hello","world");
总的一点说来:声明一个函数指针后,给该函数指针变量赋值也用对应类型函数的函数名或对函数名取址都可,调用时,直接用该函数指针变量或对该变量解引用也都可。需要注意的是,函数指针不能发生不同类型之间的转换,但可赋值nullptr或者0,的整型常量表达式,表示该函数指针没有指向任何一个函数。注意函数类型的匹配一定是函数返回值和形参类型和个数精确匹配。

说道精确匹配,这里是要求严格一致的精确匹配,当遇到函数重载时,函数指针只能选用精确匹配的那个函数。比如:

void ff(int*);
void ff(unsigned int);
void (*pf1) (unsigned int)=ff/  //这里pf1指向ff(unsigned int)
-------------------------------------------------------------------------------

三、能作为函数参数的函数指针形参

  我们知道,数组是不能作为函数形参的,函数也一样,但函数的形参可以是一个指向某个函数的指针,就像函数的形参可以是指向某个数组的指针一样。也和数组一样,形参看起来是一个函数类型,但实际上会当成一个指向这样类型的函数指针。比如:

//下面函数的声明中第三个参数就为函数类型,会自动转换为指向函数的指针
void Function(const string& s1,const& s2,bool pf(const string&,const string&));
//和上面等价,这里显示地将形参定义为了函数指针
void Function(const string& s1,const string& s2,bool (*pf)(const string &,const string&));
正如我们前面提及的,我们可以直接将函数作为实参使用,函数名就像数组名一样,当它传递给函数时会自动转换为一个指针,比如:

Function(s1,s2,LengthCompare);
在上面,我们看到Function的声明好长,看起来不爽,这是因为我们在说明一个函数类型时需要给出函数的返回值和函数的形参列表,而为了说明某种类型,我们在常规场合下可使用typedef来定义类型的别名,在这里同样适用。

首先我们可用typedef来定义自己的类型,比如:

typedef bool Func(const string&,const string&);  //这里的Func是一个函数类型
typedef bool(*FuncP)(const string&,const string&);  //这里的FuncP是一个指向函数的指针
啊,typedef的功劳使得这两句中的Func 和 FuncP是一个类型名,而不是简单的一个名,但同时,我们还有办法从变量名中推出类型,比如:

typedef decltype(LengthCompare) Func2; //定义了一个函数类型别名,它和LengthCompare函数的类型一样
typedef decltype(LengthCompare)* FuncP2; //使用decltype推导出LengthCompare函数的类型,然后加上个* ,于是说明FuncP2是一个指向这种函数类型的指针类型
注意这里的decltype返回的是函数的类型,因为我们是推导LengthCompare,它不会将函数类型自动转换为函数指针类型,所以还要加上*才能得到指向这个函数类型的指针。现在我们的Function定义可以这样:

void Function(const string&,const string&,Func);//这里,Func表示的是函数类型,可编译器可自动将它转换为函数指针,注意只有当Func是一个函数类型时可这样,简单的函数名不可以,所以,一定要记得typedef定义函数类型
void Function(const string&,const string&,FuncP2);//FuncP2在这里是一个显示的函数指针类型,也是要记得用typedef结合decltype导出一个函数指针的类型,因为形参是要给出一个类型的嘛?所以typedef在包含有函数指针的函数声明中骑到了简化作用
-------------------------------------------------------------------------------

四、返回指向函数的指针

  我们不仅可以使用函数指针,而且还可以返回函数指针,就像可以返回数组指针那样。先来讨论类型别名的另一种写法:

using F=int(int*,int);  //这里F是函数类型,不是函数指针类型(啊,函数类型的说明只要给出返回类型,参数列表就OK了)
using FP=int(*)(int*,int);//这里FP就是一个函数指针类型了
注:我们在声明一个函数时,不管是参数列表也好还是返回值也好,都对应着一种类型,若是函数指针参数或返回函数指针,那么它们也对应着要给出类型说明,但直接给出往往显得难看不美观,于是我们使用using或typedef定义类型别名。

现在我们知道,形如int(int*,int)是一种函数类型,而形如 int(*)(int*,int)是一种函数指针类型

我们的F是函数类型别名了,FP则是函数指针类型别名。和将函数名传递给函数不一样,函数返回一个函数指针时就不会将函数名转换为指针,必须显式地将返回类型指定为指针。比如:

FP f1(int);  //正确,FP是一个指向函数的指针类型,f1返回指向函数的指针
F f1(int);  //错误,F是函数类型,f1不能返回一个函数,就是不能返回一个数组一样
F* f1(int);//正确,显式地指定了返回类型是指向函数的指针
这样完全写也行:

int(*f1(int))(int*,int);
这个看起来比较纠结,从里往外看,f1有形参列表是一个函数,f1前有*,说明这个f1函数返回一个指针,再看,返回的指针类型本身也有形参列表,因为是一个指向函数的指针,再看这个函数指针的类型为 int(int* ,int)。更美观的我们是应该使用尾置返回类型的方式声明一个返回函数指针的函数:

auto f1(int)->int(*)(int*,int);

牢记,当decltypee作用于某个函数时,返回的是函数类型,而不是函数指针类型,一定要显式地加上*以表明所需要的返回指针或声明函数时的函数指针形参类型。

-------------------------------------------------------------------------------

五、练习题

  编写4个函数,它们都分别对两个int值进行加减乘除,将指向这四个函数的函数指针存放在vector中,调用vector中的每个元素并输出结果:

#include<iostream>
#include<vector>
using namespace std;
int Func(int a, int b);
int Add(int a, int b)
{
	return a + b;
}
int Sub(int a, int b)
{
	return a - b;
}
int Multi(int a, int b)
{
	return a*b;
}
int Div(int a, int b)
{
	return a / b;
}
int main()
{
	using FuncType = decltype(Func);   
	vector<FuncType*> vec{ Add, Sub, Multi, Div };  //记得这里要加* 
	int a, b;
	cin >> a >> b;
	for (auto f : vec)
		cout << f(a, b) << endl;
}