- STL提供函数对象(function obeject)
- 提供一种方法,将要调用的函数与准备传递给这个函数的隐式参数捆绑起来
函数对象表示一种操作,通过组合函数对象可以得到复杂的操作
- 运行时处理大量的循环和条件语句、调用函数的程序块的组合对效率不利
一个例子
- 标准库包含一个find_if的函数,以一对迭代器和一个判断式(predicate, 生成布尔值的函数)为参数
- 返回迭代器限定的范围内第一个使判断式得到真值的迭代器的值
|
|
find_if(v.begin(), v.end(), greater1000)返回指向v中第一个大于1000的元素的指针
函数对象和函数对象配接器使得可以不必真正定义一个函数
- 考虑模板类greater: 充当判断式,接受两个参数,判断第一个参数是否大于第二个
|
|
- 标准库一个函数配接器: bind2nd
- 函数对象f有两个参数,一个值v
- bind2nd创建一个新的函数对象g,
g(x)具有和f(x, v)相同的值
- bind2nd创建一个新的函数对象g,
bind2nd(gt, 1000)
|
|
- 用匿名对象
greater<int>()取代局部变量gt
|
|
- 表达式
bind2nd(greater<int>(), 1000)等价于greater1000(n) find_if(v.begin(), v.end(), bind2nd(greater<int>(), x))
原来的greater1000不通用
函数对象配接器解决问题
- 把信息从使用函数对象的部分通过程序的另一部分(该部分对要传递的信息(如find_if函数体)一无所知)传递到到第三部分中(如与bind2nd有关的判断表达式)
- 在第三部分中信息被取出来
函数指针
某些编程语言中函数是第一级值(first-class value)
可以将函数作为参数传递,作为值返回,当作表达式的组件使用
- C++不属于这类语言
- 函数作为参数时实际上是函数指针
|
|
- 任何试图声明函数类型的变量都被转换成指向函数的指针声明
- 所有对函数指针的调用都等价于对这个指针所指向函数的调用
- 事实上等价于
|
|
- 函数和函数指针的差异
- 不可能通过操纵函数指针创建指针所指向的对象
- C++的总存储空间在程序执行前就固定了
无法在程序开始运行后创建新函数
如何写一个C++函数把两个函数组合起来生成第三个函数
|
|
int (*h)(int) = compose(f, g)对任意整数n,h(n)将会等于f(g(n))
C++没有提供直接实现的方法
|
|
- C++不支持嵌套函数
- result需要在块作用域内访问f和g
- 也不能简单地使result全局化: f和g在result中没有定义
|
|
- 假设C++允许嵌套函数,仍然没用
|
|
- 将f和g的地址复制到两个局部变量中
- 返回指向result的指针
- fp和gp是局部变量,compose返回就无效
- 返回的result试图调用,导致程序运行崩溃
编写compose函数需要常规的基于堆栈的实现外还需要某种自动内存管理
把函数当作第一级值处理的语言通常也支持垃圾回收机制
函数对象
如果compose不能返回一个函数,可以返回一个行为和函数类似的类对象
函数对象
- 包含operator()成员函数的类
- 可以把类对象当作函数使用
|
|
- 通过这种技巧可以组合函数
|
|
- k可以用Intcomp组合已有的函数f和g
|
|
- 从原理上解决组合问题
- 只能组合函数,不能组合函数对象
函数对象模板
- 即可组合函数又可组合函数对象
|
|
- 将类型为F的函数(函数对象)与另一个类型为G的函数(函数对象)组合起来
- 得到参数类型为X,结果类型为Y的对象
|
|
- 若组合函数fg和f
Comp<Comp<int (*)(int), int (*)(int), int, int>, int (*)(int), int, int> fgf(fg, f)
- 非常复杂
隐藏中间类型
- 类型隐含在组合函数的参数的类型中:
Composition<int, int> fg(f, g);
|
|
- 构造函数必须能够接受函数和函数对象的任何组合(包括它自身)
- 构造函数必须是一个模板
|
|
- 类型F和G不属于类Composition的类型,在这里不是模板参数
一种类型包罗万象
- 重写Comp使
Comp<F, G, X, Y>继承自某个不依赖于F或者G的其他类Comp_base<X, Y> 在类Composition中可以保存指向
Comp_base<X, Y>的指针Comp_base<X, Y>接受X参数并返回Y结果的任意函数对象- 提供虚函数
operator(): 纯虚函数 - 需要虚析构函数
- 复制Comp类型对象的纯虚函数
|
|
- 用Comp_base作基类重写Comp:
|
|
- 令Composition包含一个指向Comp_base的指针
|
|
- 复制对象时对指针类型的处理: 复制底层对象
- 显式复制构造函数和析构函数
|
|
实现
- 构造函数用一个指向
Comp<F, G, X, Y>的指针初始化类型为Comp_base<X, Y>的成员p
|
|
- 析构函数只删除p所指向的对象
|
|
- 拷贝构造函数和赋值操作符使用基类Comp_base中的纯虚函数clone
|
|
operator()覆盖基类Comp_base中的纯虚函数
|
|
- 可以这样调用
|
|
Summary
绕过一个看似简单的语言局限所需要的大量工作
- 扩展语言以使之允许函数组合不像看上去那么简单
标准函数库函数transform: 对序列中的每个元素都运用函数或者函数对象,获得新序列
transform(a, a+100, a, f);
- 使序列的每一个元素都加上一个整数n
|
|
transform(a, a+100, a, Add_an_integer(n));函数配接器使得可以在联合的过程中定义类似Add_an_integer的类,而不必编写类的定义
《C++沉思录(Cplusplus Thinking)》笔记