简体   繁体   中英

Reusing objects in functions defined in C++ header

I have a function library in a header file, which includes the following function:

// Get a normally distributed float value in the range [0,1].
inline float GetNormDistrFloat()
{
    std::random_device _RandomDevice;
    std::normal_distribution<float> _NormalDistr(0.5, 2.0);

    float val = -1;
    do { val = _NormalDistr(_RandomDevice); } while(val < 0.0f || val > 1.0f);

    return val;
}

This works well, however, I don't want to create the std::random_device and std::normal_distribution objects every time I call this function GetNormDistrFloat() .

What is the "best" (correct) way in C++ to deal with this? I tried to just move those two object definitions outside the function, but that led to linker errors. Do I have to create a .cpp file for this header and initialize the objects there?

You could mark them as static variables which makes them behave almost like globals but only accessible inside the function:

void bar() {
    static Foo foo_instance;
    // Foo gets initialized only once
}

The main difference is the initialization. Globals get initialized at startup and static variables at their first access.

You can also make them globals, just make sure you do not define them in a header file, instead declare them as external:

// Header file
extern Foo foo_instance;

// Cpp file
Foo foo_instance;

Initialization of local static objects is thread-safe, however everything else is not.

I'm am not a fan of other solutions mentioned here; such as using globals or static locals. For one, state in functions is not a good idea as it's implicit and not obvious when reading code. It also makes things more complicated if you want to use the function from multiple threads. And it also makes testing more complicated. Instead, the "correct" way to handle state is to do the boring thing and create a class:

class NormDistrFloatGenerator
{
public:
    NormDistFloatGenerator(const std::random_device& device,
                           const std::normal_distribution<float>& normal)
      : m_device(device)
      , m_normal(normal)
    {}

    float get_float() { // use member variables with same logic as in question }

private:
    std::random_device m_device;
    std::normal_distribution<float> m_normal;
};

At least if you write this class, you can test it properly, or use it in multiple threads. You only have to initialize this class once, and then you can repeatedly generate floats. If you really want to have something convenient, you can then do:

NormDistFloatGenerator& void makeGlobalFloatGenerator() {
    static NormDistFloatGenerator(std::random_device, std::normal_distribution<float>(0.5, 2.0);
}

// at namespace scope
auto& g_float_generator = makeGlobalFloatGenerator();

You can then use g_float_generator everywhere. I'd really encourage you to avoid this approach. And even more so avoid the shortcuts others are suggesting.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM