C++随机数生成函数中随机类对象加static的作用及不同修饰情况差异
你给出的示例代码如下:
#include <iostream> #include <random> int gen_ran(void) { static std::random_device rd; static std::mt19937 gen(rd()); static std::uniform_int_distribution<int> dist(0, 9); return dist(gen); } int main() { for (int i = 0; i < 50; i++) { std::cout << gen_ran() << " "; if ((i + 1) % 10 == 0) std::cout << std::endl; } }
关于static修饰符作用的疑问解答
你之前的理解完全正确,三个对象加static后确实只会在第一次调用gen_ran()时完成初始化,后续所有调用直接复用已有实例,既保证了伪随机数生成器的状态连续,也避免了重复初始化的性能开销。
下面分不同场景说明差异:
场景1:完全不使用static修饰
每次调用gen_ran()都会重新创建三个对象,调用结束后立即销毁,会存在两个明显问题:
- 性能损耗:
std::random_device初始化需要读取系统熵源、std::mt19937初始化需要完成种子迭代的前置运算,两者开销都不低,高频调用时资源浪费非常明显 - 随机质量大幅下降:
std::mt19937是伪随机数生成器,依赖内部状态迭代生成连续的随机序列,每次重新播种初始化相当于每次都只取序列的第一个值,很容易出现连续重复的结果,完全无法发挥伪随机生成器的特性
场景2:仅给1-2个对象加static修饰
不同组合的效果差异如下:
- 仅给
std::random_device rd加static:只节省了rd的初始化开销,gen和dist每次依然重复创建,性能损耗、随机质量差的问题完全没有解决 - 仅给
std::mt19937 gen加static:每次调用都会用新创建的rd给gen重新播种,gen的内部状态被反复重置,随机质量差的问题依然存在,仅节省了gen的构造开销 - 仅给
std::uniform_int_distribution dist加static:没有任何实际收益,rd和gen每次重复创建的问题完全保留 - 给rd和gen加static、dist不加:运行效果和三个都加static完全一致,仅多了dist每次构造销毁的极小开销,
uniform_int_distribution的构造成本极低,这种写法也可以正常运行 - 给gen和dist加static、rd不加:第一次调用时rd给gen播种完成后,后续新建的rd不会再被使用,运行效果和三个都加static一致,仅多了rd每次读取系统熵源的不必要开销
- 给rd和dist加static、gen不加:每次gen都会重新初始化播种,随机质量差的问题依然存在
额外补充说明
C++11及以后标准中,函数内static变量的初始化是线程安全的,多线程环境下调用gen_ran()也不会出现初始化冲突的问题。如果需要固定随机序列复现问题,只要将gen(rd())的参数改为固定整数种子即可,不需要修改static修饰逻辑。
内容的提问来源于stack exchange,提问作者Ethan
相关产品推荐
相关产品推荐

