如何高效将std::shared_ptr副本传入仅接受const void*的C函数/线程
要在仅接受void*参数的C风格接口中传递std::shared_ptr副本,核心需求是保证引用计数正确维护,同时尽量减少额外开销。你的现有方法可行,但可以从效率和安全性两方面优化。
现有方法的优缺点
你当前通过new std::shared_ptr<My1>创建堆上的副本,传入线程后再delete:
- ✅ 逻辑简单,能正确增加引用计数,保证对象在线程处理期间存活
- ❌ 多了一次不必要的堆分配(
std::shared_ptr本身仅占2个指针大小,堆分配的开销相对其自身来说是额外负担)
更高效的优化方案
方案1:利用std::unique_ptr简化内存管理(安全优先,效率接近原方法)
用std::unique_ptr管理堆上的std::shared_ptr,避免手动delete,同时保持效率:
#include <iostream> #include <memory> #include <pthread.h> struct My1 { My1() { std::cout << "My1 constructor" << std::endl; } ~My1() { std::cout << "My1 destructor" << std::endl; } }; void* thread1(void* pParam) { // 用unique_ptr接管所有权,自动释放 std::unique_ptr<std::shared_ptr<My1>> pp1(static_cast<std::shared_ptr<My1>*>(pParam)); std::cout << "thread1 ref count: " << (*pp1).use_count() << std::endl; // 线程结束后,unique_ptr自动销毁堆上的shared_ptr,引用计数减1 return nullptr; } int main() { std::shared_ptr<My1> p1 = std::make_shared<My1>(); std::cout << "ref count: " << p1.use_count() << std::endl; auto pp1 = std::make_unique<std::shared_ptr<My1>>(p1); pthread_t thr1; if (pthread_create(&thr1, nullptr, thread1, pp1.get()) != 0) { // 创建失败,unique_ptr自动释放堆内存 std::cerr << "Failed to start thread" << std::endl; } else { // 创建成功,释放unique_ptr的所有权,让线程接管 pp1.release(); } std::cout << "ref count: " << p1.use_count() << std::endl; int i; std::cin >> i; return 0; }
方案2:无额外堆分配(极致高效,但有前提)
如果主线程可以保证传递的std::shared_ptr对象在整个线程执行期间始终存活,可以直接传递栈上std::shared_ptr的地址,完全避免堆分配:
#include <iostream> #include <memory> #include <pthread.h> struct My1 { My1() { std::cout << "My1 constructor" << std::endl; } ~My1() { std::cout << "My1 destructor" << std::endl; } }; void* thread1(void* pParam) { std::shared_ptr<My1>& p1 = *static_cast<std::shared_ptr<My1>*>(pParam); std::cout << "thread1 ref count: " << p1.use_count() << std::endl; // 使用p1,注意主线程不能提前销毁p1 return nullptr; } int main() { std::shared_ptr<My1> p1 = std::make_shared<My1>(); std::cout << "ref count: " << p1.use_count() << std::endl; pthread_t thr1; if (pthread_create(&thr1, nullptr, thread1, &p1) != 0) { std::cerr << "Failed to start thread" << std::endl; } else { // 必须等待线程结束,否则p1销毁后线程访问会出错 pthread_join(thr1, nullptr); } std::cout << "ref count: " << p1.use_count() << std::endl; return 0; }
⚠️ 注意:这个方案的局限性很强——主线程必须等待线程执行完毕,否则栈上的p1被销毁后,线程会访问悬空引用,导致未定义行为。
结论
- 如果主线程无法等待线程结束,方案1是最优选择:既保留了原方法的正确性,又通过
std::unique_ptr简化了内存管理,效率几乎和原方法一致(仅多了unique_ptr的构造/析构,开销可忽略)。 - 如果主线程可以等待线程结束,方案2能实现极致高效,完全避免额外堆分配。
内容的提问来源于stack exchange,提问作者c00000fd
相关产品推荐
相关产品推荐

