函数指针及其的运用(上)——何为函数指针

=========================引子=========================

      我们都知道,数组名就是指向数组第一个元素的常量指针(详见《数组拾遗》)。同理,对于一个函数而言,函数名也是指向函数第一条指令的常量指针。而编译器要做的就是在程序编译之后,为每个函数分配一个首地址,即该函数第一条指令的地址。一般情况下,我们可以用一个指针来保存这个地址,而这个指针就是函数指针,该指针可以看作是它指向函数的别名,所以我们可以用该指针来调用这个函数。

=========================函数指针的声明方法=========================

type (*func)(type &,type &)

  该语句声明了一个指针func,它指向了一个函数,这个函数带有了2个type型参数并返回一个type的值。

p.s. type类型可以被看成是int啊或者是floast等C++的类型。     

=========================注意事项=========================

  1. 一个指向函数的指针必须确保该函数被定义且分配了内存,否则它将指向一个空地址,这个可是大忌!
  2. 特别注意第一个括号的位置。如果我们不写括号,如下:
type *func(type ,type)

  这就不是一个指向函数的指针了,而是声明了一个函数,该函数返回一个type类型的指针 

 =========================函数指针应用示例=========================

      我们以这样一个程序为例:该程序的功能是计算三角形的矩形的面积。其中,三角形的长和高,矩形的长和宽由用户自己输入,并且我们提供一个交换函数,用来交换用户输入的长和高(宽)。

//例1 普通函数示例
#include <iostream>
using namespace std;
//函数声明
double triangle_area(double &x,double &y);//三角形面积
double rectangle_area(double &x,double &y);//矩形面积
double swap_value(double &x,double &y);//交换值
double set_value(double &x,double &y);//设定长宽(高)
double print_area(double &x,double &y);//输出面积
//函数定义
double triangle_area(double &x,double &y)
{
	return x*y*0.5;
}

double rectangle_area(double &x,double &y)
{
	return x*y;
}

double swap_value(double &x,double &y)
{
	double temp;
	temp=x;
	x=y;
	y=temp;
	return 0.0;
}

double print_area(double &x,double &y)
{
	cout<<"执行函数后:\n";
	cout<<"x="<<x<<"  y="<<y<<endl;
	//coming soon in e.g.2...
	return 0.0;
}

double set_value(double &x,double &y)
//注意参数一定要定义成引用,要不是传不出去哈!
{
	cout<<"自定义长宽(高)为:\n";
	cout<<"长为:";
	cin>>x; 
	cout<<"宽或者高为:";
	cin>>y;
	return 0.0;
}

int main()
{
	bool quit=false;//初始化退出的值为否
	double a=2,b=3;//初始化两个参数a和b
	char choice;
	while(quit==false)
	{
		cout<<"退出(q); 设定长、宽或高(1); 三角形面积(2); 矩形面积(3); 交换长宽或高(4)."<<endl;
		cin>>choice;
		switch(choice)
		{
			case 'q': 
				quit=true;
				break;
			case '1':
				set_value(a,b);
				print_area(a,b);
				break;
			case '2':
				print_area(a,b);
				cout<<"三角形的面积为:\t"<<triangle_area(a,b)<<endl;
				break;
			case  '3':
				print_area(a,b);
				cout<<"矩形的面积为:\t"<<rectangle_area(a,b)<<endl;
				break;
			case '4':
				swap_value(a,b);
				print_area(a,b);
				break;
			default:
				cout<<"请按规矩出牌!"<<endl;
		}
	}
	return 0;
}

  在这个例子中,我们采用普通函数大方法,来输出三角形和矩形的值,输出如下:

      下面,我们来看看如果采用函数指针,效果会是怎样?由于我们在前面分析过了,函数指针就是一个指向函数的指针。那么我们在调用函数的时候,就可以运用指针来调用这个函数。而且,周所周知,指针可以作为一个函数的参数,那么函数指针也不应该例外。这样就好了,我们可以讲函数的指针作为函数参数来调用。对于一个普通要调用指针的函数而言,声明应该是这个样子的:

type func(type*, type , type)

  那么由上面这句话就可以看出来,这个函数func的第一个参数就是一个指向type类型的指针,而后面两个参数就是两个类型为type的形式参数。结合本文一开始所列的函数参数的声明格式,那么函数指针作为函数参数的一般形式就是:

type func(type(*p)(type &, type &),type &,type &);

  该函数func有3个参数,第一个参数为type(*p)(type &, type &),这就是一个函数指针,他指向一个带有两个type类型的参数并且返回type值的函数,另外两个参数都是type类型的引用。

      这样一来,我们就可以利用函数指针把函数作为另外一个函数的参数调入到那个函数中使用了。对于上面这个例子,我故意把所有的函数都生声明为带有两个double类型的变量,且返回值均为double。这样做的原因只是为了让后面我们在利用函数指针调用的时候方便一些。因为我们只需要将函数指针声明成与之想匹配的函数就行了。从上面这个例子看出,它麻烦就麻烦在每次输出的时候都需要在case语句中进行操作,那么我们能不能利用函数指针将其一并简化到print函数中呢,请看下例:

//例2 函数指针示例
#include <iostream>
using namespace std;
//函数声明
double triangle_area(double &x,double &y);//三角形面积
double rectangle_area(double &x,double &y);//矩形面积
double swap_value(double &x,double &y);//交换值
double set_value(double &x,double &y);//设定长宽(高)
// double print_area(double &x,double &y);//输出面积
double print_area(double(*p)(double&,double&), double &x,double &y);//利用函数指针输出面积
//函数定义
double triangle_area(double &x,double &y)
{
	cout<<"三角形的面积为:\t"<<x*y*0.5<<endl;
	return 0.0;
}

double rectangle_area(double &x,double &y)
{
	cout<<"矩形的面积为:\t"<<x*y<<endl;
	return 0.0;
}

double swap_value(double &x,double &y)
{
	double temp;
	temp=x;
	x=y;
	y=temp;
	return 0.0;
}

double print_area(double(*p)(double &x,double &y), double &x,double &y)
{
	cout<<"执行函数前:\n";
	cout<<"x="<<x<<"  y="<<y<<endl;
	//it is coming!...
	p(x,y);
	cout<<"函数指针传值后:\n";
	cout<<"x="<<x<<"  y="<<y<<endl;
	return 0.0;
}

double set_value(double &x,double &y)
//注意参数一定要定义成引用,要不是传不出去哈!
{
	cout<<"自定义长宽(高)为:\n";
	cout<<"长为:";
	cin>>x; 
	cout<<"宽或者高为:";
	cin>>y;
	return 0.0;
}

int main()
{
	bool quit=false;//初始化退出的值为否
	double a=2,b=3;//初始化两个参数a和b
	char choice;
	//声明的p为一个函数指针,它所指向的函数带有梁个double类型的参数并且返回double
	double (*p)(double &,double &);
	while(quit==false)
	{
		cout<<"退出(q); 设定长、宽或高(1); 三角形面积(2); 矩形面积(3); 交换长宽或高(4)."<<endl;
		cin>>choice;
		switch(choice)
		{
		case 'q': 
			quit=true;
			break;
		case '1':
			p=set_value;
			print_area(p,a,b);
			break;
		case '2':
			p=triangle_area;
			print_area(p,a,b);			
			break;
		case  '3':
			p=rectangle_area;
			print_area(p,a,b);
			break;
		case '4':
			p=swap_value;
			print_area(p,a,b);
			break;
		default:
			cout<<"请按规矩出牌!"<<endl;
		}
	}
	return 0;
}

      在例2中,我们采用了函数指针的方式,可以看到,在case语句中只需要制定每个函数指针所指向的函数是什么,那么在print函数中,我们就可以调用这个函数了。输出如下所示:

      在该程序的第61行,我们就声明了一个函数指针。可以看到,在程序的case语句中,我们只需要把这个函数指针传递到print_area()函数里面就可以了。这样十分方便。而且我们可以看到,其实只要在print_area()中,即程序的第38行加上对这个函数指针的调用,我们就可以利用指针所指向的函数了。这十分方便。但是有几个小知识点应该注意一下:

  1. 声明函数指针时,其返回值,参数个数,参数类型应该与需要它指向的函数保持一致;否则,编译器会报错,无法从“***”转换到“***”;
  2. 利用函数指针只想某个函数的时候,我们只用,也只能给出该函数的函数名,不能把参数一并给出了。比如说在上例中,如果我们把程序的第84行改成:
    p=swap_value(a,b);
    

     那么编译器会报错:
    func_pointer.cpp(84) : error C2440: “=”: 无法从“double”转换为“double (__cdecl *)(double &,double &)
    这个错误的原因就是因为我们忘记了在文章一开头所讲的函数指针的一句话:函数名也是指向函数第一条指令的常量指针。因为函数指针就是指向其函数的地址的,那么我们就应该利用函数指针来指向函数名就可以了。

=========================补充一点哈 ^_^=========================

如果你认为上面所诉的函数指针的声明格式有点罗嗦,那么我们也可以利用typedef来简化声明和定义的操作。比如说在上例2的第61行,那么长一串。我们完全可以在在程序一开始利用typedef来代替:

typedef double (*vp)(double &,double &);

  这样一来,我们就可以把程序的第61行简化成:

vp p;

  而且,我们在声明和定义print_area()函数的时候,就可以程序的第10行和第33行换成:

//函数声明
double print_area(vp,double &x,double &y);
//函数定义
double print_area(vp p, double &x,double &y)

  好了,关于函数指针就总结到这里,更多内容请关注“唯一的天空”其他内容,谢谢 ^_^。

敬请期待:博文《函数指针及其的运用(下)——函数指针的扩展》

                                                                             coming soon...

                        

posted @ 2011-07-27 22:34  uniqueliu  阅读(75142)  评论(6编辑  收藏  举报