C ++ must be aware that article 18th sometimes requires something that acts like a function pointer, but the function pointer looks clumsy, dangerous, and out-of-date (let us acknowledge this ). Generally, the best way is to replace the function pointer with a function object. Like smart pointers (see section 42 "smart Pointers"), function objects are also common class objects. Intelligent pointer type overload-> and * (possibly-> *) operators to simulate pointer behavior, while function object types overload function call operators (), to create something similar to a function pointer. Consider the following function object. Every call to it calculates the well-known Fibonacci series (1, 1, 2, 3, 5, 8, 13 ,......) The following element value:
class Fib {
public:
Fib() : a0_(1), a1_(1) {}
Int operator (); // the original book lacks ()
private:
int a0_, a1_;
};
Int Fib: operator () {// the original book lacks ()
int temp = a0_;
a0_ = a1_;
a1_ = temp + a0_;
return temp;
}
Function objects are common class objects, but standard function call syntax can be used to call its operator () members (this member may have multiple overloaded versions ). FIB ;//...... STD: cout <"next two in series:" <fib () <''<fib () <STD: Endl; FIB () the syntax is recognized by the compiler as a call to the operator () member function of the FIB object, which means the same as fib. operator () is equivalent, but it looks more concise. In this example, the advantage of using a function object instead of a function or function pointer is that the state of the next value used to calculate the Fibonacci series is stored in the FIB object itself. If a function is used for computing, you must turn to global or local static variables or other basic techniques to maintain the state between function calls, or pass the status information explicitly to the function. It should also be noted that, unlike the functions using static data, we can have multiple fib objects for simultaneous calculation without interfering with the calculation process and results.
int fibonacci () {
static int a0 = 0, a1 = 1; // problematic...
int temp = a0;
a0 = a1;
a1 = temp + a0;
return temp;
}
It is also possible and common to obtain the virtual function pointer, which is achieved by creating a function object hierarchy with virtual operator. Consider a numerical integration software that is used to calculate the approximate value of the area surrounded by a curve, as shown in: Numeric integration by summing areas of rectangles (simplified) an integral function can call a function repeatedly for the value between low and high to approximate the area surrounded by the curve, this is achieved by calculating the sum of the rectangular area (of course, some similar mechanisms can be used ):
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;
}
In this version, a function pointer is passed to perform the expected integral operation.
double aFunc( double x ) { ... }
//...
double area = integrate( aFunc, 0.0, 2.71828 );
This is feasible, but not flexible, because it uses a function pointer to indicate the function to be integrated. It cannot process functions in the desired state or pointers to member functions. An alternative method is to create a function object hierarchy. The base class of this hierarchy is a simple interface class, and only one pure virtual operator () function is declared. Class Func {public: virtual ~ Func (); virtual double operator () (double) = 0 ;}; double integrate (Func & f, double low, double high ); currently, integrate can be used with any type of Func function object (the so-called "Func function object of any type", which indicates any type of Func derived class instance .) For integration. Another interesting thing to note is that the integrate function body does not need to be modified (re-compiled once is inevitable ), because we use the same syntax as calling a function pointer to call a function object. For example, you can derive a type from Func that can process non-member functions: class NMFunc: public Func {public: NMFunc (double (* f) (double )): f _ (f) {} Double operator () (double d) {return f _ (d);} private: double (* f _) (double ); // define a function pointer member}; this allows the original version of integrate to integrate all functions:
double aFunc( double x ) { ... }
//...
NMFunc g( aFunc );
double area = integrate( g, 0.0, 2.71828 );
By packaging an appropriate interface for pointers and class objects pointing to member functions, you can also integrate member functions (see "pointers to member functions are not Pointers-Clause 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 );