有时需要一些行为类似于函数指针的东西,但函数指针显得笨拙、危险而且过时(让我们承认这一点)。通常最佳方式是使用函数对象(function object)取代函数指针。

  与智能指针一样,函数对象也是一个普通的类对象。智能指针类型重载 -> 和 * (可能还有 ->* 和 .*)操作符,来模仿指针的行为;而函数对象类型则重载函数调用操作符“()”,来创建类似于函数指针的东西。考虑如下函数对象,它的每次调用都计算众所周知的斐波拉切数列(1,1,2,3,5,8,13,...)的下一个元素值:

class Fib
{
public:
Fib():a0_(),a1_(){}
int operator()();
private:
int a0_,a1_;
};
int Fib::operator()()
{
int temp=a0_;
a0_ = a1_;
a1_ = temp + a1_;
return temp;
}

  函数对象就是常规的类对象,但是可以采用标准的函数调用语法来调用它的operator()成员(此成员可能具有多个重载版本)。

Fib fib;
cout<<"next two in series:"<<fib()<<' '<<fib() <<' '<<fib()<<' '<<fib()<<' '<<fib() <<' '<<fib()<<' '<<fib() <<' '<<fib()<<' '<<fib()<<endl;

  fib()语法被编译器识别为对fib对象的operator()成员函数的调用,这在意思上和fib.operator()等价,但是看起来更简洁。在这个例子中,使用函数对象而不是函数或者函数指针的优势在于,用于计算斐波拉切数列下一个值的状态被存储于Fib对象自身之中。如果采用函数来实现计算功能,那么必须求助于全局或局部静态变量或其它一些基本的技巧,以便在函数调用之间保持状态,或者将状态信息明确传递给函数。还要注意的是,有别于使用静态数据的函数,我们可以拥有多个同时计算的Fib对象,且其计算过程和结果不会相互干扰。

  还有可能并且常见的是获得虚函数指针的效果,这是通过创建一个带有虚拟operator()的函数对象层次结构而实现的。考虑一个数值积分软件,它用于计算曲线所包围面积的近似值,如图5所示。

        

  一个积分函数可以对 low 和 high 之间的值反复调用一个函数,来近似计算曲线所包围的面积,这是通过计算矩形面积的总和而实现的(当然也可以采用一些类似的机制):

typedef double (*F)(double);
double integrate(F f,double low, double high){
const int numsteps = ;
double step = (high - low)/numsteps;
double area = 0.0;
while( low < high) {
area += f(low)*step;
low += step;
}
return area;
}

  在这个版本中,传递一个函数指针来执行所期望的积分操作。

double fsinx(double x){
return sin(x);
}
//...
cout<<"sin函数[0,π]与x轴围成的面积"<<integrate(fsinx,,PI)<<endl;

  这可行,但不灵活,因为它使用一个函数指针来指示待整合的函数。它不能处理需要状态的函数或者指向成员函数的指针。一个代替的方式是创建一个函数对象层次结构。该层次结构的基类是一个简单的接口类,只声明了一个纯虚函数operator()。

class Func{
public:
virtual ~Func(){};
virtual double operator()(double) =;
}; double integrate(Func &f,double low ,double high){
const int numsteps = ;
double step = (high - low)/numsteps;
double area = 0.0;
while( low < high) {
area += f(low)*step;
low += step;
}
return area;
}

  现在integratge能够与任何类型的Func函数对象进行整合。还有一个值得注意的有趣的地方,就是integrate函数体不需要任何修改(当然,重新编译一遍是免不了的),因为我们使用与调用函数指针相同的语法来调用一个函数对象。例如,可以从Func派生出一个可以处理非成员函数的类型:

class NMFunc:public Func{
public:
NMFunc ( double (*f)( double )) : f_(f){}
double operator()( double d ){return f_( d );}
private:
double (*f_)( double ); };

  这就允许最初版本的integrate整合所有的函数:

double fsinx(double x){
return sin(x);
}
//...
NMFunc g(fsinx);
cout<<"sin函数[0,2/π]与x轴围成的面积"<<integrate(g,,PI/)<<endl;

  通过为指向成员函数的指针和类对象包装一个适当的接口,还可以将成员函数整合进来:

template<class C>
class MFunc : public Func{
public:
MFunc(C c, double (C::*f)( double ))
:obj_(c),f_(f){};
double operator()( double d ) { return (obj_.*f_)(d);}
private:
C &obj_;
double (C::*f_)( double );
};
//...
class MuFunc{
public:
double fcos(double x){return cos(x);}
};
//...
class MuFunc{
public:
double fcos(double x){return cos(x);}
};
//...
MuFunc mufunc;
MFunc<MuFunc> f(mufunc,&MuFunc::fcos);
cout<<"cos函数[0,π]与x轴围成的面积"<<integrate(f,,PI)<<endl;

此部分整个测试代码如下(vc++6.0):

#include<iostream>
#include<math.h>
using namespace std; #define PI 3.1415926 class Fib
{
public:
Fib():a0_(),a1_(){}
int operator()();
private:
int a0_,a1_;
};
int Fib::operator()()
{
int temp=a0_;
a0_ = a1_;
a1_ = temp + a1_;
return temp;
}
typedef double (*F)(double);
double integrate(F f,double low, double high){
const int numsteps = ;
double step = (high - low)/numsteps;
double area = 0.0;
while( low < high) {
area += f(low)*step;
low += step;
}
return area;
}
double fsinx(double x){
return sin(x);
} class Func{
public:
virtual ~Func(){};
virtual double operator()(double) =;
}; class NMFunc:public Func{
public:
NMFunc ( double (*f)( double )) : f_(f){}
double operator()( double d ){return f_( d );}
private:
double (*f_)( double ); }; double integrate(Func &f,double low ,double high){
const int numsteps = ;
double step = (high - low)/numsteps;
double area = 0.0;
while( low < high) {
area += f(low)*step;
low += step;
}
return area;
} template<class C>
class MFunc : public Func{
public:
MFunc(C c, double (C::*f)( double ))
:obj_(c),f_(f){};
double operator()( double d ) { return (obj_.*f_)(d);}
private:
C &obj_;
double (C::*f_)( double );
}; class MuFunc{
public:
double fcos(double x){return cos(x);}
}; int main()
{
Fib fib; cout<<fib()<<' '<<fib()<<' '<<fib()<<' '<<fib()<<' '<<fib()<<' '<<fib()<<endl;
cout<<"next two in series:"<<fib()<<' '<<fib()
<<' '<<fib()
<<' '<<fib()
<<' '<<fib()
<<' '<<fib()
<<' '<<fib()
<<' '<<fib()
<<' '<<fib()<<endl; cout<<"sin函数[0,π]与x轴围成的面积"<<integrate(fsinx,,PI)<<endl; NMFunc g(fsinx);
cout<<"sin函数[0,2/π]与x轴围成的面积"<<integrate(g,,PI/)<<endl; MuFunc mufunc;
MFunc<MuFunc> f(mufunc,&MuFunc::fcos);
cout<<"cos函数[0,π]与x轴围成的面积"<<integrate(f,,PI)<<endl;
return ;
}

 

  

函数对象[条款18]---《C++必知必会》的更多相关文章

  1. django基础之day04,必知必会13条,双下划线查询,字段增删改查,对象的跨表查询,双下划线的跨表查询

    from django.test import TestCase # Create your tests here. import os import sys if __name__ == " ...

  2. SQL 必知必会

    本文介绍基本的 SQL 语句,包括查询.过滤.排序.分组.联结.视图.插入数据.创建操纵表等.入门系列,不足颇多,望诸君指点. 注意本文某些例子只能在特定的DBMS中实现(有的已标明,有的未标明),不 ...

  3. H5系列之History(必知必会)

    H5系列之History(必知必会)   目录 概念 兼容性 属性 方法 H5方法       概念     理解History Api的使用方式 目的是为了解决哪些问题   作用:ajax获取数据时 ...

  4. Java面试必知必会:基础

    面试考察的知识点多而杂,要完全掌握需要花费大量的时间和精力.但是面试中经常被问到的知识点却没有多少,你完全可以用 20% 的时间去掌握 80% 常问的知识点. 一.基础 包括: 杂七杂八 面向对象 数 ...

  5. mysql必知必会

    春节放假没事,找了本电子书mysql必知必会敲了下.用的工具是有道笔记的markdown文档类型. 下面是根据大纲已经敲完的章节,可复制到有道笔记的查看,更美观. # 第一章 了解SQL## 什么是S ...

  6. 《MySQL必知必会》整理

    目录 第1章 了解数据库 1.1 数据库基础 1.1.1 什么是数据库 1.1.2 表 1.1.3 列和数据类型 1.1.4 行 1.1.5 主键 1.2 什么是SQL 第2章 MySQL简介 2.1 ...

  7. 脑残式网络编程入门(三):HTTP协议必知必会的一些知识

    本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ...

  8. msql 必知必会笔记

    Edit Mysql 必知必会 第一章 理解SQL 什么是数据库 数据库(database) 保存有组织的数据的容器 什么是表  一组特定类型的数据的结构化清单 什么是模式  数据库和表的布局及特性的 ...

  9. SQL语法语句总结(《SQL必知必会》读书笔记)

    一.SQL语句语法 ALTER TABLE ALTER TABLE 用来更新已存在表的结构. ALTER TABLE tablename (ADD|DROP column datatype [NULL ...

随机推荐

  1. db2 导入cvs

    1)       打开DB2 命令行 2)       如果是第一次连接到远程的DB2 数据库:如果不是,请直接跳转到3) 在DB2 命令行窗口执行: catalog tcpip node DB21 ...

  2. 我在Facebook学到的10个经验

    1.坚持你的远景,但要对细节灵活. 作为一个领导者,你需要依赖你自己的远景(至少在你负责的业务领域内)而那些和你一起或为你工作的人将依赖你的远见.什么是远景?就是对最终状态的一种描述.是你需要你的团队 ...

  3. Docker 如何把镜像上传到docker hub

    1 首先你得准备一个hub 的帐号, 去 https://hub.docker.com 注册吧! 2 在hub那里新建一个仓库, 这个就类似于github那边的..create ---> cre ...

  4. 总结界面框架_UI_Adapter

    本人定期更新经典案例及解决方案如有疑问请联系我QQ1822282728 -- 277627117   下面是常用到的ui  Demo 安卓三级筛选菜单listview(非常经典) http://dow ...

  5. 【BZOJ3626】[LNOI2014]LCA 离线+树链剖分+线段树

    [BZOJ3626][LNOI2014]LCA Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度 ...

  6. 通过pymysql程序debug学习数据库事务、隔离级别

    问题 今天在使用pymysql连数据库的时候,出现了一个bug,查询数据库某个数据,但是在我在数据库中执行sql语句改变数据后,pymsql的查询依然没有发生改变. 代码如下: # 5.6.10 co ...

  7. kettle中denormalizer(列转行)的使用

    转载: 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://sucre.blog.51cto.com/1084905/1434015 ...

  8. Python--进阶处理1

    # ===============Python 进阶======================= # ---------第一章:数据结构和算法----------- # ----------解压序列 ...

  9. 后端UI框架

    BootStrap EasyUI DWZ ExtJS

  10. Linux 常用资源

    kernel:ftp://kernel.orgcnkernel:http://www.cnkernel.orgoldlinux:http://www.oldlinux.orgminix3:http:/ ...