C++ Primer Plus书之--C++函数指针

函数指针

使用函数指针, 能完成这样的工作:可以编写将另一个函数的地址作为参数的函数, 这样第一个函数就能找到第二个函数, 并且运行它, 与直接调用第一个函数相比, 这种方法很笨拙, 但他允许在不同的时间传递不同函数的地址, 这意味着可以再不同的时间使用不同的函数.

基本需要包含以下步骤:

1.获取函数的地址

2.声明一个函数指针

3.使用函数指针来调用函数

第一步: 获取函数的地址:

获取函数地址很简单, 只需要使用函数名(后面不要跟参数)即可. 例如:think 是一个函数

// 这样就会把函数think的地址传递给函数process
process(think);

// 这样是先调用think函数, 将think的返回值传递给process函数
thought(think());

第二步:声明函数指针

声明指向某种数据类型的指针时, 必须指定指针指向的类型. 同样声明指向函数的指针时, 也必须指定指针指向的函数类型. 这意味着声明指定函数的返回类型以及函数的参数列表, 例如:

// 函数原型, 原型中可以不指定形参变量
double test(int);

// 正确的指针类型声明如下:
// 意思是pfun指向一个需要一个int型参数的返回double类型的函数
double (*pfun)(int);

注意:与test()函数原型类似, 就是将test替换为(*pfun), 由于test是函数, 因此(*pfun)也是函数, 而如果(*pfun)是函数, 则pfun就是指向函数的指针.

通常, 要声明指向特定类型的函数的指针的时候, 可以先写这种函数的原型, 然后用(*pfun)替换函数名, 则pfun就是指向函数的指针.

注意: 必须在声明中使用()将*pfun括起来, 括号的优先级比*高, 因此*pfun(int), 表示pfun是一个返回指针的函数, 而(*pfun)(int)表示fpun是指向函数的指针.

正确声明pfun后, 就可以将相应函数的地址赋给pfun:

pfun = test;

注意test()的参数列表和返回值, 必须和pfun的相同, 否则会报错.

现在来写一个完整函数调用另一个函数的函数声明:

// 声明estimate接收一个指向一个接收int型参数,并且返回double类型的函数的指针
void estimate(int lines, double (*pfun)(int));

// 使用estimate很简单:
// pam是函数名, 就是需要传递进函数estimate的函数的函数名
estimate(50, pam);

第三步:使用指针来调用函数.

前面说过(*pfun)与函数名的作用相同, 因此使用时只需将(*pfun)看做函数名即可.

// 这种写法虽然不太好看, 但是明确的显示出来, 正在使用的是函数指针
double x = (*pfun)(2);

// c++也支持这种写法, 就像使用函数名那样使用pfun
double y = pfun(3);

来看一个完整的例子:

// 函数指针例子
#include <iostream>

using namespace std;

// 函数原型
double betsy(int);
double pam(int);

// 第二个参数值一个函数指针, 指向一个需要一个int型参数, 并且返回double的函数
void estimate(int lines, double (*pfun)(int));

int main() 
{
	int code;
	cout << "How many lines of code do you need? ";
	cin >> code;
	cout << "Here's Betsy's time : " << endl;
	// 函数名就是函数的地址, 所以直接将函数名传进去
	estimate(code, betsy);
	cout << "Here's pam's time : " << endl;
	estimate(code, pam);
	return 0;
}

double betsy(int lines)
{
	return 0.02 * lines;
}

double pam(int lines)
{
	return 0.03 * lines;
}

void estimate(int lines, double (*pfun)(int))
{
	cout << lines << " lines will take : ";
	// 或者写成这样也行: cout << pfun(lines) << " hours " << endl;
	cout << (*pfun)(lines) << " hours " << endl;
}

看一下运行结果:

深入探讨函数指针

1.声明三个函数原型, 他们的参数列表和返回类型都相同:

const double * f1(const double arr[], int n);
const double * f2(const double [], int n);
const double * f3(const double *, int n);

const double arr[] 和 const double *arr相同而变量名可以省略, 因此上面三个函数原型的参数列表和返回值都一样

 

2.声明一个函数指针, 指向上面声明的三种函数其中的任何一种

const double *(pf)(const double *, int);

并且可以在声明的同事进行初始化:

const double *(pf)(const double *, int) = f1;

使用c++11的自动类型推断功能时, 代码要简单的多:

auto p2 = f2;

 

3.再来看下面这句:

cout << (*p1)(av, 3) << " : " << *(*p1)(av, 3) << endl;
cout << p2(av, 3) << " : " << *p2(av, 3) << endl;

(*p1)(av, 3)和p2(av, 3)都是调用指向的函数f1和f2, 并将av和3作为参数, 因此这两个显示的是函数的返回值, 也就是double值得地址, 而要显示double值则使用后面的代码即*(*p1)(av, 3) 和 *p2(av, 3)

 

鉴于需要使用三个函数, 如果有一个函数的指针数组将会很方便, 如何声明这样的数组呢?

const double * (*pf[3])(const double *, int) = {f1, f2, f3};

为何要将[3]放在这个地方呢? pf是一个包含了三个元素的数组, 因此要声明这样的数组, 首先要pf[3], 该声明的其他部分指出了数组包含的元素是什么样的. 运算符[]的优先级高于*, 因此*pf[3]是一个包含三个指针的数组, 上述声明的其他部分指出了每个指针指向的是什么, 参数列表为const double *, int且返回值是double *的函数.

我们既然已经声明了pa, 那么再声明同样的数组就可以使用atuo了

auto pb = pa;

因为数组名是指向数组的第一个元素的指针, 因此pb和pa都是指向函数指针的指针
可以使用如下的方法来调用函数:

const double * px = pa[0](test, 3);
const double * py = (*pb[1])(test, 3);

要获得对应的double值, 可以使用运算符*

double x = *(pa[0](test, 3));
double y = *((*pb[1])(test, 3));

再看下面这种声明: 

auto pc = &pa;

如果我们想自己声明pc的话, 需要这样做: 
// 这是一个指针, 指向了一个包含三个元素的数组
(*pd)[3]
// 这是一个数组, 里面包含了三个指针
*pd[3]
主要是涉及到优先级的问题, *的优先级比[]低, 所以第二个首先是个数组, 然后前面的*指明了它里面的内容是指针, 第一个首先是(*pd)说明是个指针, 然后[]说明指针指向了一个包含三个元素的数组.

换句话说, pd是一个指针, 它指向一个包含三个元素的数组:

const double* (*(*pd)[3])(double *, int) = &pa;

要调用函数, 需要明确的是既然pd是指向数组, 那么*pd就是数组, 而(*pd)[i]就是数组中的元素即函数指针, 因此简单的调用方式是(*pd)[i](arr, 3), 而*(*pd)[i](arr, 3)就是返回的指针指向的值. 也可以使用指针调用函数的语法(*(*pd)[i])(arr, 3)来调用函数(按照面介绍的既然(*pd)[i], 相当于函数名, 那么函数指针就可以按照在函数名前加*来调用函数, 也就是(*(*pd)[i])),    *(*(*pd)[i])(arr, 3)就是指向的double的值.

下面看一下完整的例子
 

// 深究函数指针
#include <iostream>
using namespace std;
// 以三种不同的方式生命了三个函数原型, 实际他们的函数格式都是一样的, 只是函数名不同
// 就是函数的参数列表和函数的返回值都是一样的
const double* f1(const double arr[], int n);
const double* f2(const double [], int n);
const double* f3(const double *, int n);

int main()
{
	double d_arr[3] = {1.23, 2.34, 3.45};
	
	// 定义了一个函数指针
	// 指向一个接收const double *, 和int型 两个参数的, 并且返回double *的函数
	// 然后把函数f1赋值给了p1这个指针
	const double* (*p1)(const double *, int) = f1;
	// 利用auto声明了一个函数指针
	// 这个函数指针实际和声明p1的时候一样, 只不过auto比较省事
	auto p2 = f2;
	// const double* (*p2)(const double *, int) = f2; 上面auto和这行意义一样
	
	cout << "Using pointers to functions: " << endl;
	cout << "Address Value" << endl;
	cout << p1(d_arr, 3) << " : " << *(p1(d_arr, 3)) << endl;
	cout << (*p2)(d_arr, 3) << " : " << *((*p2)(d_arr, 3)) << endl;
	
	// 声明了一个包含三个指针的数组
	// 而数组里的指针是指向接收const double *和int型参数, 并且返回double *的函数
	// 这句话不能直接使用auto, auto不适用列表初始化
	const double * (*pa[3])(const double *, int) = {f1, f2, f3};
	
	// 但是仍然适用于单个值的初始化
	// pb是一个指针, 指向pa的第一个元素
	auto pb = pa;
	// c++11 之前可以用下面这行, pb是一个指针的指针
	// const double * (**pb)(const double *, int) = pa;
		
	cout << "Using an array of pointers" << endl;
	cout << "Anddress Value" << endl;
	
	for (int i = 0; i < 3; i++)
		cout << (pa[i])(d_arr, 3) << " : " << (*(pa[i])(d_arr, 3)) << endl;
	
	cout << "Using a pointer to a pointer to a function : " << endl;
	cout << "Anddress Value" << endl;
	for(int i = 0; i < 3; i++)
		cout << pb[i](d_arr, 3) << " : " << *pb[i](d_arr, 3) << endl;
	
	cout << "Using pointers to an array of pointers: " << endl;
	cout << "Address  Value" << endl;
	auto pc = &pa;
	// const double * (*(*pc)[3])(const double *, int) = &pa;
	
	cout << (*pc)[0](d_arr, 3) << " : " << *(*pc)[0](d_arr, 3) << endl;
	
	
	const double * (*(*pd)[3])(const double *, int) = &pa;
	const double * pdb = (*pd)[1](d_arr, 3);
	cout << pdb << " : " << *pdb << endl;
	
	cout << (*(*pd)[2])(d_arr, 3) << " : " << *(*(*pd)[2])(d_arr, 3) << endl;

	return 0;
}

const double* f1(const double arr[], int n)
{
	return arr;
}

const double* f2(const double arr[], int n)
{
	return arr + 1;
}

const double* f3(const double * arr, int n)
{
	return arr + 2;
}

看一下运行结果:

猜你喜欢

转载自blog.csdn.net/c1392851600/article/details/84641160