1

I have a class dieClass to represent a six-sided die, and I use <random> to set the number. Here is the header file die.h:

#include <random>
#include <iostream>

class dieClass
{
public:
    dieClass();
    int getNum() const { return num; }

private:
    int num;
    static std::uniform_int_distribution<int> distribution;
};

and here is the implementation file die.cpp:

#include "die.h"

dieClass::dieClass()
{
    static std::random_device rdevice{};
    static std::default_random_engine generator{rdevice()};
    num = distribution(generator);
}

std::uniform_int_distribution<int> dieClass::distribution{1, 6};

Question: If I call dieClass dice[5], have I created five default_random_engines, or just one because it is static? Basically, what would be the most efficient way to construct millions of dice? Can I declare generator outside of the constructor, like I did for distribution? I don't fully understand what private and static do.

EDIT: Rearranging things like this seems to achieve what I want, although it may not be best practice. I pulled all random-number-generating code from the class and stuck it in the implementation file. Now I can call generator from a function roll.

dieClass.h
#include <iostream>

class dieClass
{
public:
    die();
    void roll();
    int getNum() const { return num; }

private:
    int num;
};
dieClass.cpp
#include <random>
#include "die.hpp"

std::random_device rdevice{};
std::default_random_engine generator{rdevice()};
std::uniform_int_distribution<int> distribution{1, 6};

dieClass::dieClass()
{
    num = distribution(generator);
}

void dieClass::roll()
{
    num = distribution(generator);
}
user3100212
  • 101
  • 1
  • 5
  • Does this answer your question? [The static keyword and its various uses in C++](https://stackoverflow.com/questions/15235526/the-static-keyword-and-its-various-uses-in-c) – L. F. Feb 21 '20 at 10:29
  • 2
    It's static, so it's only one instance. My personal approach would be to not use static members, but let the die class have a generator, a distribution and function to "throw" the die, that generates a new random number. Your current approach seems to be modelling the result of die throw not an actual die that can be thrown many times. But that's just my opinion. – Lukas-T Feb 21 '20 at 10:32
  • 2
    I would go for the `int getNum() { return distribution(generator); }` approch. Why do you need million of instances of a class that does nothing but randomize 1 number for you? Just call a function and return a different random value each time. – super Feb 21 '20 at 10:34
  • Those members shouldn't be static. Reusing the generators for multiple die objects completely defeats the purpose of using `random` in the first place. If you want to share those, then, you can just share an object of `dieClass`. – Bartek Banachewicz Feb 21 '20 at 11:19
  • The second example is the best on. Static is not commonly used and rarely needed. If you won't to create the randomization as a global instances (like in the 2nd example) you can encapsulate it in a class that menage everything, including the creation of dices (dieclass). – TheArchitect Feb 21 '20 at 12:07
  • 1
    Slightly OT, but seeding a PRNG with the current time is a dangerous practice that is only ok when the PRNG has absolutely no connection to security. For a purely local game, it's fine. But already when the game allows people to upload their high-scores, a clock dependent seed allows for cheating. If you need random numbers of any quality, use `/dev/urandom` or comparable. – cmaster - reinstate monica Feb 21 '20 at 13:30
  • Instead of `static`, consider a `thread_local` generator to avoid data races – Caleth Feb 21 '20 at 13:58

1 Answers1

1

If you are going to have millions of dice I prefer your second example. It will be more efficient to have one random generator. However, instead of a global random number generator for your dice you can encapsulate it inside a class.

Like static variables you should avoid the use of globals as much as possible. It is possible to inject a reference or pointer to this new generator class to your dieClass. Just change the constructor to accept it. Whenever you want to generate a new number just call a method of the new class.

Here's an example:

#include <random>
#include <iostream>

class RandomNumberGenerator
{
public:
    RandomNumberGenerator() : mRd(), mGen(mRd()), mDis(1, 6) {}
    ~RandomNumberGenerator() = default;

    inline int Generate() { return mDis(mGen); }

private:
    std::random_device mRd;
    std::mt19937 mGen;
    std::uniform_int_distribution<int> mDis;
};


class dieClass
{
public:
    dieClass(RandomNumberGenerator &gen) : mGenerator(gen), num(gen.Generate()) {}
    int getNum() const { return num; }
    void roll() { num = mGenerator.Generate(); }

private:
    RandomNumberGenerator &mGenerator;  //store as reference to avoid copying
    int num;
};

This way it's the responsibility of the RandomNumberGenerator class to generate the randome number, not the dieClass class.

Working version here.

jignatius
  • 6,304
  • 2
  • 15
  • 30