FAQ 16.12 What is the right strategy for implementing a function that needs to maintain state between calls?
In C, it was common to create a function that
Viewing a function with local static data as a global functionoid object makes it clear why the static data is expensive to model (global
int rand() throw()
{
static unsigned long current = 1001;
<-- 1
current = current * 22695477UL + 1;
return int(current >> 12) & 0x7fff;
}
The static variable
current
introduces subtle dependencies between users of the function. Any change in the calling pattern can alter the behavior of this routine. Such routines are notorious in
A better way to do this is with a class. Every
#include <iostream>
using namespace std;
class RandomSequence {
<-- 1
public:
RandomSequence(int initialSeed=1001) throw();
int next() throw();
protected:
unsigned long current_;
};
RandomSequence::RandomSequence(int initialSeed) throw()
: current_(initialSeed) { }
int RandomSequence::next() throw()
{
current_ = current_ * 22695477UL + 1;
return int(current_ >> 12) & 0x7fff;
}
The user gets a sequence of random numbers by using the member function next() .
void printRandomSequence() throw()
{
RandomSequence rand;
for (int i = 0; i < 10; ++i)
cout << rand.next() << ' ';
}
int main()
{ printRandomSequence(); }
Before, there was a global
rand()
function with a single state variable. Now there is a local
rand
object and as many state variables as there are user functions that want an independent pseudorandom sequence. The dependencies among callers (and
Another reason to create a functionoid object is when a function
|
FAQ 16.13 How can the function call operator help with functionoids?
The
function call operator
lets users
In the previous example, class
RandomSequence
is a functionoid. Unlike a standard function,
RandomSequence
can maintain state between calls without sharing that state between all of its
Functionoids often use the function call operator (
operator()()
) rather than a named member function such as
#include <iostream>
using namespace std;
class RandomSequence {
public:
RandomSequence(int initialSeed=1001) throw();
int operator()() throw();
<-- 1
protected:
unsigned long current_;
};
RandomSequence::RandomSequence(int initialSeed) throw()
: current_(initialSeed) { }
int RandomSequence::operator()() throw()
{
current_ = current_ * 22695477UL + 1;
return int(current_ >> 12) & 0x7fff;
}
Given an object of class RandomSequence called rand , users can now use rand() instead of rand.next() :
int main()
{
RandomSequence rand;
for (int i = 0; i < 10; ++i)
cout << rand() << ' ';
}
|