c++ 函数指针

*****************************
在有些编程语言中,函数是“第一级值”。在这些语言中,可以将函数作为函数参数
传递,并把它们当作表达式的组件使用等。
c++不属于这类语言,但这一点并不是显而易见的。因为对于c++程序而言,将函数作为
参数传递,并把它们的地址存储在数据结构中是很常见的操作。
例如,假设我们想对某个数组的所有元素都运用某个给定函数。如果这个函数有一个int
参数,并且生成void类型,我们可以如下编写代码
*****************************
void apply(void f(int), int *p, int n) {
for (int i = 0;i < n;i++) {
f(p[i]);
}
}
*****************************
这是不是说明c++把函数也当作第一级值呢?
本例中第一个隐蔽之处就是,f虽然看上去像函数,其实根本就不是函数。相反它是一个
函数指针。和在c中一样c++不可能有函数类型的变量,所以任何声明这种变量的企图都将
立即被转换成指向函数的指针声明。和在c中一样,所有对函数指针的调用都等价于这个
指针所指向的函数的调用。所以前面的例子就等价于
*****************************
void apply(void (*fp)(int), int *p, int n) {
for (int i = 0;i < n;i++) {
(*fp)(p[i]);
}
}
*****************************
那又怎么样?函数和函数指针之间的有什么重大差异吗?这个差异和任何和任何指针与
其所指向的对象之间的差异是类似的:不可能通过操纵指针创建这样的对象。c++函数的总
存储空间在程序执行之前就固定了。一旦程序开始运行,就无法创建新函数了。为了理解
为什么说不能动态创建新函数是个问题,我们来思考一下如何写一个c++函数,以便把两个
函数组合起来生成第三个函数。组合是我们所能想到的创建新函数最简单的方法之一。现在
为了简单期间,我们将假设每个函数都有一个整数参数并返回一个整数结果。然后,假设我
们有一对函数f和g:
extern int f(int);
extern int g(int);
我们希望能够使用下面的语句:
int (*h)(int) = compose(f,g);
具有一种特征,就是对于任何整数n而言,h(n)将等价于f(g(n))。
c++没有提供直接做这件事的方法。我们可以杜撰如下的代码:
int (*compose(int f(int), int g(int) ) )(int x) {
int result(int n) { return f(g(n));}
return result;
}
这里,compose试图用两个函数f和g来定义一个函数,当应用于x时可以得到f(g(x))的函数;
但是由于两个原因它不可能成功。第一个原因就是c++不支持嵌套函数,这意味着result的定义
非法。而且由于result需要在块作用域之内访问f和g,所以没有简便的方法可以绕过这个限制。
简单的使result全局化:
int result(int n) {return f(g(n));}
int (*compose(int f(int), int g(int))) (int x)

return result;

这个例子的问题在于f和g在result中没有定义。
第二个问题更难以捉摸。假设c++允许嵌套函数——毕竟c++实现把它当成一种扩展,那么这样
做会成功吗?
可惜的是,答案是“实际不会成功”。为了了解原因,我们稍微修改了一下compose函数:
*****************************

int (*compose (int f(int), int g(int)))(int x) {
int (*fp)(int) = f;
int (*gp)(int) = g;
int result (int n) {return fp(gp(n));}
return result;
}
*****************************
其中所做的改变是将f和g的地址复制到两个局部变量fp和gp中去。现在,假设我们调用compose,
它将返回一个指向result的指针。因为fp和gp是compose的局部变量,所以一旦compose返回它们
就消失了。如果我们现在调用result,它将试图使用这些局部变量,但是这些变量已经被删除了。
结果很可能导致程序运行崩溃
显然编写compose的最后一个版本,我们应该很容易明白这个程序失败的原因。然而,第一个版本
也存在相同的问题。唯一的不同的是第一个版本中的f和g不是普通的局部变量,而是形参。这个
区别无关大局:当compose返回时它们也消失;也就是说当result试图访问它们时也会导致崩溃。
那么显然编写compose函数除了需要常规的基于堆栈的实现外,还需要某种自动回收机制。尽管
c++将垃圾回收集作为语言的标准部分会给很多方面带来好处,但是存在太多的困难使我们不能这
样定义c++。


write by fgd

posted @ 2012-03-12 19:19  wen_dao_  阅读(253)  评论(0编辑  收藏  举报