C++中mt19937对象按引用传递仍生成相同随机序列的问题
问题描述
在类A中创建mt19937随机数生成器对象,供A内部使用,同时将该对象的引用传递给多个类B实例,期望这些B实例能共享同一个生成器生成不同随机序列,但实际每个B实例生成的随机序列完全相同。
相关代码与编译信息如下:
类A代码
A.h
#include <random> #include <vector> #include "B.h" using namespace std; class A { public: A(); private: mt19937 mt; };
A.cpp
#include "A.h" A::A() : mt((random_device())()) { vector<B> Bs; for(int i = 0; i < 10; i++) { Bs.emplace_back(i, mt); } }
类B代码
B.h
#include <random> #include <iostream> using namespace std; class B { public: B(int id, mt19937& mt); private: int id; mt19937 mt; };
B.cpp
#include "B.h" B::B(int id, mt19937& mt) { this->id = id; this->mt = mt; bernoulli_distribution dist(0.5); cout << "Testing random..." << endl; for(int i = 0; i < 10; i++) { cout << dist(this->mt) << " "; } cout << endl; }
main代码
main.cpp
#include "A.h" int main() { A a; return 0; }
编译命令:
g++-12 B.cpp A.cpp main.cpp -o main -std=c++17
运行环境:Mac OS Monterey 12.2.1,Macbook Pro M1
问题原因
类B的私有成员mt19937 mt;是独立对象,构造函数中this->mt = mt;是复制了传入的mt19937对象的状态。由于所有B实例在A的构造函数中连续创建时,A的mt还未被修改过,每个B实例复制得到的mt状态完全一致,因此生成的随机序列完全相同。且后续A的mt状态改变时,B实例的复制版本也不会同步。
解决方案
要让所有B实例共享同一个mt19937对象,需将类B的成员改为引用或指针,而非独立对象。
方案1:使用引用
修改B.h
将私有成员改为引用类型,注意引用必须通过初始化列表初始化,不能在构造函数体内赋值:
#include <random> #include <iostream> using namespace std; class B { public: B(int id, mt19937& mt); private: int id; mt19937& mt; };
修改B.cpp
通过初始化列表初始化引用成员:
#include "B.h" B::B(int id, mt19937& mt) : id(id), mt(mt) { bernoulli_distribution dist(0.5); cout << "Testing random for B " << id << "..." << endl; for(int i = 0; i < 10; i++) { cout << dist(this->mt) << " "; } cout << endl; }
方案2:使用指针
若需后续灵活更换生成器,可使用指针:
修改B.h
#include <random> #include <iostream> using namespace std; class B { public: B(int id, mt19937* mt); private: int id; mt19937* mt; };
修改B.cpp
#include "B.h" B::B(int id, mt19937* mt) : id(id), mt(mt) { bernoulli_distribution dist(0.5); cout << "Testing random for B " << id << "..." << endl; for(int i = 0; i < 10; i++) { cout << dist(*this->mt) << " "; } cout << endl; }
修改A.cpp中创建B实例的代码
Bs.emplace_back(i, &mt);
注意事项
- 使用引用时,需确保被引用的
mt19937对象生命周期长于所有B实例,避免悬空引用。本场景中A的mt成员生命周期覆盖B实例,因此安全。 - 使用指针时,需注意空指针检查,避免访问无效内存。
random_device在部分平台可能返回固定序列(如无硬件随机源的环境),但这并非当前问题的诱因。
内容的提问来源于stack exchange,提问作者Bryan Barbosa
相关产品推荐
相关产品推荐

