函数对象 (Function Objects)

函数对象(Function Objects)

C++必知必会 18条款

 

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

      

       与智能指针(参见条款42"智能指针")一样,函数对象也是一个普通的类对象。智能指针类型重载->*(可能还有->*)操作符,来模仿指针的行为;而函数对象类型则重载函数调用操作符(),来创建类似于函数指针的东西。考虑如下函数对象,它的每次调用都计算众所周知的斐波纳契数列(11235813,……)的下一个元素值:

class Fib {
  public:
    Fib() : a0_(1), a1_(1) {}
    int operator ()();       //书的原著中缺少了一个()
  private:
    int a0_, a1_;
};
int Fib::operator ()() {     //书的原著中缺少了一个()
    int temp = a0_;
    a0_ = a1_;
    a1_ = temp + a0_;
    return temp;
}

      

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

       Fib;

//……

       std::cout << “next two in series: ” << fib()

                     << ‘  ’ << fib() << std::endl;

 

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

 

int fibonacci () {
    static int a0 = 0, a1 = 1; // problematic...                      
    int temp = a0;
    a0 = a1;
    a1 = temp + a0;
    return temp;
}

 

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

Numeric integration by summing areas of rectangles (simplified)

 

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

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

 

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

double aFunc( double x ) { ... }
//...
double area = integrate( aFunc, 0.0, 2.71828 );

 

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

    class Func {

             public:

                     virtual ~Func();

                     virtual double operator()(double) = 0;

       };

       double integrate(Func &f, double low, double high);

 

       现在integrate能够与任何类型的Func函数对象(--所谓任何类型的Func 函数对象,中指任何类型的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 aFunc( double x ) { ... }
//...
NMFunc g( aFunc );
double area = integrate( g, 0.0, 2.71828 );

 

       通过为指向成员函数的指针和类对象包装一个适当的接口,还可以将成员函数整合进来(参见“指向成员函数的指针并非指针条款 16 ):

template <class C>
class MFunc : public Func {
  public:
    MFunc( C &obj, double (C::*f)(double) )
        : obj_(obj), f_(f) {}
    double operator ()( double d )
        { return (obj_.*f_)( d ); }
private:
    C &obj_;
    double (C::*f_)( double );
};
//...
AClass anObj;
MFunc<AClass> f( anObj, &AClass::aFunc );
double area = integrate( f, 0.0, 2.71828 );

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值