advantage of function object:
You may be wondering what all this is good for. You might even think that function objects look strange, nasty, or nonsensical. It is true that they do complicate code. However, function objects are more than functions, and they have some advantages:
1. Function objects are "smart functions."
Objects that behave like pointers are smart pointers. This is similarly true for objects that behave like functions: They can be "smart functions" because they may have abilities beyond operator (). Function objects may have other member functions and attributes. This means that function objects have a state. In fact, the same function, represented by a function object, may have different states at the same time. This is not possible for
ordinary functions. Another advantage of function objects is that you can initialize them at runtime before you use/call them.
2. Each function object has its own type.
Ordinary functions have different types only when their signatures differ. However, function objects can have different types even when their signatures are the same. In fact, each functional behavior defined by a function object has its own type. This is a significant improvement for generic programming using templates because you can pass functional behavior as a template parameter. It enables containers of different types to
use the same kind of function object as a sorting criterion. This ensures that you don't assign, combine, or compare collections that have different sorting criteria. You can even design hierarchies of function objects so that you can, for example, have different, special kinds of one general criterion.
3. Function objects are usually faster than ordinary functions.
The concept of templates usually allows better optimization because more details are defined at compile time. Thus, passing function objects instead of ordinary functions often results in better performance.
In the rest of this post I present some examples that demonstrate how function objects can be "smarter" than ordinary functions. functional behavior as a template parameter. Suppose you want to add a certain value to all elements of a collection. If you know the value you want to add at compile time, you could use an ordinary function:
void add10 (int& elem)
{
elem += 10;
}
{
elem += 10;
}
void fl()
{
vector<int> coll;
...
for_each (coll.begin(), coll.end(), //range
add10); //operation
}
{
vector<int> coll;
...
for_each (coll.begin(), coll.end(), //range
add10); //operation
}
If you need different values that are known at compile time, you could use a template instead:
template <int theValue>
void add (int& elem)
{
elem += theValue;
}
void f1()
{
vector<int> coll;
...
for_each (coll.begin() , coll.end(), //range
add<10>); //operation
}
If you process the value to add at runtime, things get complicated. You must pass the value to the function before the function is called. This normally results in some global variable that is used both by the function that calls the algorithm and by the function that is called by the algorithm to add that value. This is messy style.
If you need such a function twice, with two different values to add, and both values are processed at runtime, you can't achieve this with one ordinary function. You must either pass a tag or you must write two different functions. Did you ever copy the definition of a function because it had a static variable to keep its state and you needed the same function with another state at the same time? This is exactly the same type of problem.
With function objects, you can write a "smarter" function that behaves in the desired way. Because the object may have a state, it can be initialized by the correct value. Here is a complete example:
#include <iostream>
#include <list>
#include <algorithm>
#include <list>
#include <algorithm>
using namespace std;
//function object that adds the value with which it is initialized
class AddValue {
class AddValue {
private:
int the Value; //the value to add
public:
//constructor initializes the value to add
AddValue(int v) : theValue(v) {
}
//the "function call" for the element adds the value
void operator() (int& elem) const {
elem += theValue;
}
};
int the Value; //the value to add
public:
//constructor initializes the value to add
AddValue(int v) : theValue(v) {
}
//the "function call" for the element adds the value
void operator() (int& elem) const {
elem += theValue;
}
};
int main()
{
list<int> coll;
The first call of for_each() adds 10 to each value:
for_each (coll.begin(), coll.end(), //range
AddValue(10)) ; //operation
Here, the expression AddValue(10) creates an object of type AddValue that is initialized with the value 10. The constructor of AddValue stores this value as the member theValue. Inside for_each(), "()" is called for each element of coll. Again, this is a call of operator () for the passed temporary function object of type AddValue. The actual element is passed as an argument. The function object adds its value 10 to each element. The elements then have the following values:
after adding 10: 11 12 13 14 15 16 17 18 19
The second call of for_each() uses the same functionality to add the value of the first element to each element. It initializes a temporary function object of type AddValue with the first element
of the collection:
of the collection:
AddValue (*coll. begin())
The output is then as follows:
after adding first element: 22 23 24 25 26 27 28 29 30
By using this technique, two different function objects can solve the problem of having a function with two states at the same time. For example, you could simply declare two function objects and use them independently:
AddValue addx (x); //function object that adds value x
AddValue addy (y); //function object that adds value y
for_each (coll.begin(),coll.end(), //add value x to each element
addx);
...
for_each (coll.begin(),coll.end(), //add value y to each element
addy);
...
for_each (coll.begin() .coll.end(), //add value x to each element
addx);
addx);
...
for_each (coll.begin(),coll.end(), //add value y to each element
addy);
...
for_each (coll.begin() .coll.end(), //add value x to each element
addx);
Similarly you can provide additional member functions to query or to change the state of the function object during its lifetime.
Note that for some algorithms the C++ standard library does not specify how often function objects are called for each element, and it might happen that different copies of the function object are passed to the elements. This might have some nasty consequences if you use function objects as predicates.
No comments:
Post a Comment