You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 10:30:52