I’m trying to create a sensible class hierarchy for a congruential random number generator class (which means that it has 3 integer parameters – M, a, b). I decided that the base class should provide an interface for derived ones (so it should be abstract), and M, a, b should be static const in derived classes (as they are the same throughout a derived class).
As generate() function is the same for all congruential random number generators, its definition should be placed inside the base class. The problem is that this function uses all of M, a, b, but these can’t be be made static const‘s in the base class.
To illustrate a problem, a provided one of the possible solutions. However, I’m not fully satisfied with it, as it creates 3 extra long long variables for each instance of a derived class, so I wonder if a more elegant design can be proposed.
class RandomGenerator{
protected:
unsigned int seed;
const long long int M;
const long long int a;
const long long int b;
public:
RandomGenerator(unsigned int, long long, long long, long long);
virtual long double generate() const = 0;
};
long double RandomGenerator::generate() const{
static long long prv = seed;
return (long double) (prv = (a * prv + b) % M) / (M-1);
}
class RandU : public RandomGenerator {
private:
static const long long M = 2147483648LL;
static const long long a = 65539;
static const long long b = 0;
public:
RandU(unsigned int);
virtual long double generate() const;
};
RandU::RandU(unsigned int nseed): RandomGenerator(nseed, M, a, b){}
long double RandU::generate() const{
return RandomGenerator::generate();
}
One way to solve this is to add a class to the hierarchy:
As you can see, I’ve derived
GeneralRandomGeneratorfromRandomGenerator. The former now contains members forM,aandb, while the latter provides pure virtual access functionsgetM(),geta()andgetb(). In theGeneralRandomGeneratorthose access functions are implemented to return the members.But in
RandU, which still derived from the highest level, the accessors are defined to return the values of the static members.This way, the
generate()function of the highest level can access what it needs through the accessor functions, while the values really come from either static or non-static members, or potentially from somewhere completely different. The advantage clearly is thatRandUwon’t take any space forM,aandbbecause those members don’t exist in it.The disadvantage however is that even in
GeneralRandomGeneratoraccess toMetc. is performed by calling a virtual function, which mean poorer performance than what would be possible by coding access to the static members directly.One way to avoid even that is to do provide the generalised for of
generate()as a separate function outside the classes and passM,aandbas arguments: