C++ 在C+中定义的函数中重用对象+;标题

C++ 在C+中定义的函数中重用对象+;标题,c++,c++11,C++,C++11,我在头文件中有一个函数库,其中包括以下函数: // 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; d

我在头文件中有一个函数库,其中包括以下函数:

// 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;
}
void bar() {
    static Foo foo_instance;
    // Foo gets initialized only once
}
//获取[0,1]范围内的正态分布浮点值。
内联float getnormdributfloat()
{
std::随机_装置_随机装置;
标准:正态分布正态分布(0.5,2.0);
float val=-1;
do{val=_NormalDistr(_RandomDevice)}而(val<0.0f | | val>1.0f);
返回val;
}
这很好,但是,我不想每次调用此函数时都创建
std::random\u设备
std::normal\u distribution
对象


《C++》中“最好”(正确)的处理方法是什么?我试图将这两个对象定义移到函数之外,但这导致了链接器错误。我是否必须为此标头创建一个.cpp文件并初始化其中的对象?

您可以将它们标记为静态变量,这使它们的行为几乎类似于全局变量,但只能在函数内部访问:

// 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;
}
void bar() {
    static Foo foo_instance;
    // Foo gets initialized only once
}
主要区别在于初始化。全局变量在启动时初始化,静态变量在第一次访问时初始化

您也可以将它们设置为全局,只需确保不在头文件中定义它们,而是将它们声明为外部:

// Header file
extern Foo foo_instance;

// Cpp file
Foo foo_instance;

本地静态对象的初始化是线程安全的,但是其他的都不是。

我不喜欢这里提到的其他解决方案;例如使用全局变量或静态局部变量。首先,函数中的状态不是一个好主意,因为它是隐式的,在读取代码时并不明显。如果要从多个线程使用函数,这也会使事情变得更加复杂。这也使得测试更加复杂。相反,处理状态的“正确”方法是做一些无聊的事情并创建一个类:

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;
};
类规范生成器
{
公众:
标准频率发生器(常数标准::随机_装置和装置,
常数标准::正态分布和正态分布)
:m_设备(设备)
,m_正常(正常)
{}
float get_float(){//使用与所讨论的逻辑相同的成员变量}
私人:
std::随机_装置m_装置;
标准:正态分布m正态分布;
};
至少如果您编写这个类,您可以正确地测试它,或者在多个线程中使用它。您只需初始化这个类一次,然后就可以重复生成浮点数。如果你真的想要一些方便的东西,你可以:

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

// at namespace scope
auto& g_float_generator = makeGlobalFloatGenerator();
NormDistFloatGenerator&void makeGlobalFloatGenerator(){
静态正态分布发生器(标准::随机分布装置,标准::正态分布(0.5,2.0);
}
//在命名空间范围内
auto&g_float_generator=makeGlobalFloatGenerator();

然后,您可以在任何地方使用
g_float_generator
。我真的鼓励您避免这种方法。更重要的是,要避免其他人建议的快捷方式。

\u random device
是一个保留标识符。请删除前导下划线。@MSalters感谢您的反馈!或者添加一个互斥锁。本地静态解决方案不再是线程-比全局安全。@molbdnilo您是对的,但是可以保证初始化是线程-safe@molbdnilo:全局的主要问题是它受到初始化顺序问题的影响。全局是按照文件中声明的顺序构造的,但这只是部分顺序(不是跨源文件)我不使用
设备
普通
对象,而是使用两个浮动(OP示例中为0.5,2.0)作为构造函数的参数。@Fozi这可能更可取,我并没有真正强调这一点,因为我不确定什么是最适合的,比如说测试需求,或者其他方面。我更想强调的是使用一个类,所以我选择了最直接映射到成员的构造函数。这个想法很好听起来,这是一个已知的随机数生成器的反模式。通过复制状态,你复制了数字序列,这有效地使它们非随机。