其实指针并不可怕,可怕的是你那一颗不自信的心和对于指针的畏惧!
说明,写在前面:
1.本文的内容严格按照C++标准而写,可能与C语言的标准有出入,C语言用户请只做参考。
2.我书写了大量的代码用于验证本文内容的正确性,我所用的编译器是微软的Visual Studio 2010 SP1(中文旗舰版)和GNU的MinGW 20111108。
3.如果读者也要写代码来验证本文内容的正确性,请使用和我一样的编译器或者使用一些版本较新的编译器。因为一些旧版本的编译器对C++标准的支持是不完整的,比如Visual C++ 6.0。用Visual C++ 6.0可能会对正确的标准C++代码报错,当然,对本文的内容仅是可能,并且可能性应该不大。我的Visual C++ 6.0装在虚拟机上,时间关系,没有在那上面测试本文的内容。
4.我的测试代码暂时不提供给读者。
5.如果没有学过C++,或者是C++初学者,或者对C++的掌握不是很精纯,那么,请不要先阅读本文,因为我怕打击你的信心。请达到一定水准后再来阅读本文。因为本文是C++的进阶文章。
正文:
在通向C++的道路上,函数与指针是不可缺少的元素,那么,当两者邂逅时,又会发生什么呢,下面就让我带你走进它们的世界。
我们将用五个部分来说明几个重要的概念:返回指针的函数,指向函数的指针(函数指针),返回指向函数的指针的函数。
注意:C++允许函数在声明时不写形参的名字,而只写形参的类型,如:
int abc(int i);在声明可以写为int abc(int);
因此本文的声明都省略形参名。但注意,在实现时是不能省略的。
1.返回指针的函数和指向函数的指针(函数指针)
返回指针的函数声明示例如下:
bool *pf(const string&, const string&);
这里,pf是一个函数,返回的是一个bool类型的指针。
指向函数的指针声明示例如下:
bool (*pf)(const string&, const string&);
这里,pf是一个指针,指向的是一个函数。这个函数的返回类型是bool,并有两个const string型的引用形参。我们把pf叫做一个函数指针。
2.用typedef简化函数指针的定义及其初始化,赋值和使用。
函数指针类型相当冗长,使用typedef为指针类型定义同义词,可将函数指针的使用大大简化:
typedef bool (*cmpFcn)(const string&, const string&);
这里,cmpFcn是一个函数指针类型,不是一个具体的指针。就像类和对象的关系一样。
现在可以用cmpFcn来定义指向返回类型是bool,并有两个const string型的引用形参的函数的指针了。
假设有一个函数:
bool lengthCompare(const string&, const string&);
在标准C++中,除了用作函数的左操作数外,对函数名lengthCompare的任何使用都被解释为如下类型的指针
bool(*)(const string&, const string&);
显然这是一个函数指针。
因此,我们可以这样:
cmpFcn pf1=0;//正确。用0值为函数指针初始化。
cmpFcn pf2=lengthCompare;//正确。用同类型的函数名为函数指针初始化。
pf1=lengthCompare;//正确。用同类型的函数名为函数指针赋值。
pf2=pf1;//正确。用同类弄的函数指针为函数指针赋值。
我在上面的注释中反复强调同类型这个词,因为函数指针的初始化和赋值只能是0值表达式或者同类型的函数或者同类型的函数指针。例如假设有函数:
string::size_type sumLength(const string&, const string&);
bool cstringCompare(char*, char*);
对以下操作就有:
cmpFcn pf;
pf=sumLength;//错误。不是同类型。
pf=cstringCompare;//错误。不是同类型。
pf=lengthCompare;//正确.同类型。
注:引用函数名相当于对函数名应用取地址操作符,因此以下两句代码是等效的:
cmpFcn pf1=lengthCompare;
cmpFcn pf1=&lengthCompare;
使用函数指针的方法:
若 cmpFcn pf=lengthCompare; 则以下三句代码等效:
lengthCompare("hi","bye");
pf("hi","bye");
(*pf)("hi","bye");
3.函数指针用作函数的形参
函数指针是可以用作函数的形参的,并且这种形参有以下两种等效的写法:
void useBigger(const string&, const string&, bool(const string&, const string&));
void useBigger(const string&, const string&, bool(*)(const string&, const string&));
这里说明,标准C++允许将函数的形参定义为一个函数类型,但是这个函数类型必须是指向函数的指针,而不能是函数,因此上面中,编译器在编译时会将
bool(const string&, const string&));自动转换为
bool(*)(const string&, const string&));
也就是说函数类型的形参所对应的实参将被自动转换为指向相应函数类型的指针。
但是有一点要注意,当返回的是函数时,同样的转换操作无法自动实现,而必须显示指明。
如:
typedef int func(int*, int);
//注意,上面定义的不是函数指针类型,而且普通函数类型
对如下操作有:
void f1(func);//正确。函数类型func将自动转换为相应的函数指针类型
func f2(int);//错误。返回值是函数,自动转换失效。
func *f3(int);//正确。返回值是函数指针不是函数。
第二句第三句的区别就是,返回函数类型时是要转换为相应函数指针的,但是不是由编译器自动完成的,必须由程序员显示指明。
4.返回指向函数的指针的函数
听起来太拗口了,但是在标准C++中,这是可以的,但是,正确地写出这种返回类型也是相当不容易的,我举个例子:
int (*ff(int))(int*, int);
我来解释这个定义:
ff(int) 这是将ff声明为一个函数,并带有一个int类型的形参。这个函数返回的是这样一个指针:
int (*)(int*, int);
如果你前面的内容看懂了,就容易发现这就是前面说的一个函数指针。
现在两者一结合,就是返回指向函数的指针的函数,其本质是函数,而不是指针。
我们同样可以用typedef来达到简化的目的。
typedef int (*PF)(int*, int);
PF ff(int);
5.指向重载函数的指针
标准C++允许函数指针指向重载的函数,但函数指针的类型必须和重载函数的某一个版本精确匹配。举例说明:
现在有两个重载函数,声明如下:
extern void ff(vector<double>);
extern void ff(unsigned int);
那么函数指针
void (*pf1)(unsigned int)=&ff;//&ff可以写成ff,等效,前面讲过
将正确匹配函数void ff(unsigned int);
void (*pf2)(int)=&ff;//错误。没有可以匹配的函数。
因为没有void ff(int);这样的函数
double (*pf3)(vector<double>)=&ff//错误。没有可以匹配的函数。
因为没有double ff(vector<double>);这样的函数。
本文完。
2012年04月28日 中国宁波