条款17 处理函数和数组声明
指向函数的指针声明与指向数组的指针声明很容易混淆,主要原因在于函数和数组修饰符的优先级比指针修饰符的优先级高,因此通常需要使用圆括号。
- int *f1(); // 一个返回值为int *的函数
- int (*fp1)(); // 一个指针,指向一个返回值为int的函数
具有高优先级的数组修饰符存在同样的问题:
- const int N = 12;
- int *a1[N]; // 一个具有N个int *元素的数组
- int (*ap1)[N]; // 一个指针,指向一个具有N个int元素的数组
理所当然,一旦拥有指向函数或数组的指针,就可以拥有指向这种指针的指针:
- int (**ap2)[N]; // 一个指针,它指向一个指针,后
者则指向一个具有N个int元素的数组 - int *(*ap3)[N]; // 一个指针,指向一个具有N个int*元素的数组
- int (**const fp2)() = 0; // 一个常量指针,指向一个指向函数的指针
- int *(*fp3)(); // 一个指针,指向一个返回值为int *的函数
注意,参数和返回值都会影响函数或函数指针的类型。 - char *(*fp4)(int,int);
- char *(*fp5)(short,short) = 0;
- fp4 = fp5; // 错误!类型不匹配
当函数和数组修饰符出现于同一个声明中时,事情的复杂性会变得难以估量。看一看如下常见但错误的声明,它试图声明一个函数指针数组:
- int (*)()afp1[N]; // 语法错误!
在以上错误的声明中,函数修饰符()的出现表示到了声明的末尾,而后面附加的afp1则表示开始出现了一个语法错误。这类似于以下的数组声明写法:
- int[N] a2; // 语法错误!
这在Java中是合法的,但在C++(www.cppentry.com)中是非法的。函数指针数组的正确声明方式,是将数组名字和简单的函数指针声明放在一起。因此可以声明一个装有如下这些东西的数组:
- int (*afp2[N])();// 一个具有N个元素的数组,
其元素类型为指向“返回值为int”的函数的指针
至此,事情开始变得不那么灵便,typedef闪亮登场的时机到了:
- typedef int (*FP)();// 一个指向返回值为int的函数的指针
- FP afp3[N];// 一个具有N个“类型为FP”的元素的数组,该类型与afp2相同
使用typedef可以简化复杂的声明语法,这也是对你的代码维护者的关爱。使用type- def,甚至标准的set_new_handler函数的声明都变得简单多了:
- typedef void (*new_handler)();
- new_handler set_new_handler( new_handler );
如此一来,new_handler(参见“函数指针” [条款14])是指向这种函数的指针:它不带任何参数,返回void。而set_new_handler则是一个函数,带有一个new_handler作为参数,并返回一个new_handler作为结果。简单吧?如果尝试不用typedef,你在代码维护者中的声望将急剧下跌。
- void (*set_new_handler(void (*)()))(); // 语法没错,但邪恶!
还可以声明函数引用:
- int aFunc( double ); // 函数
- int (&rFunc)(double) = aFunc; // 函数引用
函数引用很少使用,其应用程度跟常量函数指针差不多:
- int (*const pFunc)(double) = aFunc; // 常量函数指针
数组引用确实提供了一些数组指针所未提供的额外能力,参见“数组形参” [条款6]中的讨论。