如何高效传递与共享shared_ptr所有权?三类实现方案性能对比
先直接给结论:Option 2的实现性能最优,这和你在Visual Studio 2017里的基准测试结果完全一致。咱们逐个拆解这三个选项的底层逻辑,就能明白为什么会这样:
各选项的性能分析
Option 1:值传递+直接赋值
void setI_1(shared_ptr<int> i) { m_i = i; }
调用这个函数时,实参会先拷贝构造形参i(这会触发一次原子操作来增加引用计数),然后函数内部把i赋值给m_i,又会触发一次原子操作的拷贝。等于一次调用要做两次引用计数的原子增减,开销是最大的,完全不推荐。
Option 2:值传递+std::move
void setI_2(shared_ptr<int> i) { m_i = move(i); }
这个选项的巧妙之处在于利用了std::move转移所有权:
- 如果传入的是右值(比如临时创建的
shared_ptr<int>{new int(42)}),形参i会通过移动构造创建,这只是简单地转移指针和引用计数的所有权,没有原子操作;然后内部move(i)给m_i又是一次移动,同样无原子开销,全程几乎零额外成本。 - 如果传入的是左值(比如一个已有的
shared_ptr<int>变量),形参i会先拷贝构造(一次原子操作),但内部的move(i)会把i的所有权转移给m_i,这一步没有原子操作。相比Option 1少了一次原子拷贝,开销大幅降低。
Option 3:const引用+直接赋值
void setI_3(shared_ptr<int> const& i) { m_i = i; }
这个选项的逻辑是:通过const引用避免形参的拷贝,内部赋值给m_i时做一次拷贝(一次原子操作)。看起来和传入左值时的Option 2成本差不多,但有个明显的短板:
- 当传入右值时,const引用会绑定到临时对象,然后赋值给
m_i还是要做一次拷贝(原子操作),而Option 2对右值的处理是纯移动,完全没有原子开销,这时候Option 3的性能就会落后。这也是你测试中部分场景Option 3性能更差的原因。
总结
- 优先选Option 2,它在所有场景下都能保证最优或接近最优的性能,不管传入左值还是右值都能高效处理。
- Option 3只在传入左值的场景下性能接近Option 2,但面对右值时会有额外开销,通用性不如Option 2。
- Option 1因为双重拷贝的原子开销,性能最差,完全不建议使用。
内容的提问来源于stack exchange,提问作者user1899020
相关产品推荐
相关产品推荐

