C++中std::future未返回值引用的问题排查
C++ std::future返回string地址不一致的原因解析
我刚开始学习C++多线程,在学习过程中发现打印std::future返回的string内存地址时,地址不一致。
原始代码
#include<iostream> #include <thread> #include <future> #include <string> void thFunc(std::promise<std::string> && prms) { std::string str("Hello from future ^^"); std::cout << "Location=" << (void*)str.data() << "\n"; prms.set_value(str); } int main() { std::promise<std::string> prms; std::future<std::string> ftr = prms.get_future(); std::thread th(&thFunc, std::move(prms)); std::printf("Hello from main!!\n"); std::string futStr = ftr.get(); std::cout << futStr << "\t Location= " << (void*)futStr.data() << "\n"; th.join(); return 0; }
编译命令
clang++ -g -Wall -std=c++11 -stdlib=libc++ -o future_ex future_ex.cpp
运行输出
Hello from main!! Location=0x30cdbff01 Hello from future ^^ Location= 0x30cd3dfa1
我查阅了std::future::get的文档,了解到它使用std::move,理论上不会有拷贝,但地址仍不一致,希望得到该行为的解释。
编辑后的代码(改用shared_ptr)
void thFunc(std::promise<std::shared_ptr<std::string>> & prms) { std::string str("Hello from future ^^"); std::shared_ptr<std::string> shared = std::make_shared<std::string>(str); std::cout << "Address in thread=" << shared.get() << " String= " << *shared.get() << "\n"; prms.set_value(shared); } int main() { std::promise<std::shared_ptr<std::string>> prms; std::future<std::shared_ptr<std::string>> ftr = prms.get_future(); std::thread th(&thFunc, std::ref(prms)); std::printf("Hello from main!!\n"); std::shared_ptr<std::string> futSharedPtr = ftr.get(); std::cout << "Address in main= " << futSharedPtr.get() << " Value= " << *futSharedPtr.get() << "\n"; std::cout << "COUNT:: " << futSharedPtr.use_count() << "\n"; th.join(); return 0; }
编辑后运行输出
Hello from main!! Address in thread=0x60000298c018 String= Hello from future ^^ Address in main= 0x60000298c018 Value= Hello from future ^^ COUNT:: 1
原因解析
string地址不一致的核心原因
promise.set_value触发的第一次拷贝
线程中的prms.set_value(str)传递的是左值str,会调用set_value的左值重载,直接把str的内容拷贝到promise的共享状态中。这一步已经让字符串的存储地址从线程栈上的str变成了共享状态内部的新对象,地址自然改变。future.get的移动无法保留原始地址
调用ftr.get()时,确实会将共享状态中的string移动到主线程的futStr中,但这无法让地址和线程里的str一致:- 你的字符串长度较短,触发了libc++的小字符串优化(SSO)——短字符串直接存在string对象的内部缓冲区(栈内存),而非堆上。移动这类string时,本质是复制对象内部的字符数组,而非转移堆指针,所以地址再次变化。
- 即使是堆上的string,共享状态的内存位置和主线程
futStr的栈帧完全不同,移动操作只是把共享状态中string的堆指针转移给futStr,但这个堆指针本身就和线程里str的堆指针不一样(因为第一步已经拷贝到共享状态了)。
shared_ptr能保持地址一致的原因
shared_ptr存储的是指向堆上string对象的指针:
- 无论是
prms.set_value(shared)还是ftr.get(),传递的都是shared_ptr的拷贝(或移动),但所有shared_ptr实例都指向同一个堆上的string对象。 - 线程中的
shared在函数结束后被销毁,所以主线程的futSharedPtr的use_count()为1,但堆上string的地址始终未变,因此前后打印的地址完全一致。
内容的提问来源于stack exchange,提问作者cicada_
相关产品推荐
相关产品推荐

