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

为何向其他线程传递临时对象不会引发未定义行为?

为什么向线程传递临时对象不会触发未定义行为?

这个问题的核心其实是线程参数的拷贝时机和对象生命周期的问题,咱们先从你贴的那个有问题的代码说起:

void f(int i,std::string const& s);
void oops(int some_param) {
    char buffer[1024];
    sprintf(buffer, "%i",some_param);
    std::thread t(f,3,buffer);
    t.detach();
}

作者说这是未定义行为完全没错——你看,buffer是oops函数里的局部栈数组,当你把buffer传给std::thread的构造函数时,线程库并不会直接帮你把它转换成std::string。它做的只是拷贝这个char*类型的指针(也就是buffer的首地址),然后把这个指针存在线程内部。

麻烦就出在这儿:oops函数可能跑得比新线程快多了——说不定新线程还没来得及调用f,还没把这个指针转换成std::string,oops就已经退出了,此时buffer作为局部变量已经被栈销毁。新线程再去访问这个指针指向的内存,就是在访问已经被释放的空间,这可不就是未定义行为嘛。

那换成传递临时对象为啥就安全了?比如咱们把代码改成这样:

void f(int i,std::string const& s);
void fixed(int some_param) {
    char buffer[1024];
    sprintf(buffer, "%i",some_param);
    // 显式构造临时std::string对象传递给线程
    std::thread t(f,3,std::string(buffer));
    t.detach();
}

这里的关键变化是:临时对象的拷贝是在当前线程(也就是fixed函数所在的线程)中完成的。当你调用std::thread的构造函数时,线程库会在fixed函数还没退出、buffer还活着的时候,把这个临时std::string对象完整拷贝一份,存在线程自己的内部存储里。

等新线程开始执行f的时候,它拿到的是这个拷贝后的std::string的引用——这个拷贝的对象已经和原buffer没关系了,哪怕fixed函数退出、buffer被销毁,新线程里的std::string依然持有自己的字符数据副本,完全不受影响。

再给你提炼下核心区别:

  • 传递局部变量的指针/数组名:线程只拷贝指针,对象本身留在原线程栈上,原线程退出后对象销毁,新线程访问就炸。
  • 传递临时对象:线程在原线程中完成对象拷贝,拷贝后的对象归线程所有,生命周期独立于原函数,自然不会有悬垂引用的问题。

顺带提一句:别想着用std::ref来传递临时对象,那完全是南辕北辙——std::ref是用来传递引用的,反而会让线程直接访问原临时对象,而临时对象的生命周期可能只到线程构造结束,照样会出问题。咱们这里的场景,就是要让线程库帮我们完成拷贝,所以直接传临时对象就对了。

内容的提问来源于stack exchange,提问作者choxsword

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:39:15