shared_ptr插入vector:传值/const引用+移动的用法对比与疑问
核心行为与疑问解答
1. 两种写法的实际行为差异
先看你代码里的bar函数:
void bar(const std::shared_ptr<int>& ptr) { _vec.emplace_back(std::move(ptr)); }
这里有个关键误区:ptr是const引用,std::move(ptr)会生成const std::shared_ptr<int>&&类型的右值,但std::shared_ptr的移动构造函数只接受非const的右值引用(shared_ptr&&),编译器会直接 fallback 到拷贝构造——也就是说这段代码看起来是移动,实际是把ptr拷贝到vector里。
再看foo函数:
void foo(std::shared_ptr<int> ptr) { _vec.emplace_back(std::move(ptr)); }
ptr是传值得到的独立副本,属于非const对象,std::move(ptr)能正常触发移动构造:把ptr的资源所有权转移到vector的新元素中,ptr本身会变成空的shared_ptr。
2. 为什么两种情况sp的use_count都是2?
调用
a.foo(sp)时:- 传值过程会拷贝
sp,此时sp的引用计数从1变为2(原sp+ 函数内的ptr); - 函数内移动
ptr到vector,ptr被掏空(引用计数减1),最终引用计数回到2(原sp+ vector里的新元素); - 所以main里的
sp调用use_count()返回2。
- 传值过程会拷贝
调用
a.bar(sp)时:- 传const引用不会增加引用计数,此时
sp的计数还是1; - 因为无法移动,只能拷贝
ptr到vector,引用计数增加到2(原sp+ vector里的新元素); - 所以main里的
sp调用use_count()返回2。
- 传const引用不会增加引用计数,此时
3. vector是否存储原sp的引用?
不会。std::vector<std::shared_ptr<int>>的元素是独立的shared_ptr对象,不是引用。不管是拷贝还是移动,vector里都会生成一个新的shared_ptr实例:
- 拷贝时,新实例和原
sp指向同一资源,引用计数增加; - 移动时,新实例接管原对象的资源,原对象变成空,引用计数不变(仅所有权转移)。
你提到的“传引用时计数没增加,但移动后计数增加”,本质是因为bar里的“移动”实际是拷贝,所以计数才会增加;如果是真正的移动(比如把bar的参数改成非const引用std::shared_ptr<int>& ptr,再调用a.bar(std::move(sp))),此时原sp会被掏空,引用计数会保持1(只有vector里的新元素持有资源)。
哪种用法更合理?
优先选择
foo的传值写法:- 它能正确支持移动语义:当调用者传入右值(比如
a.foo(std::move(sp)))时,传值过程会直接移动,避免拷贝开销;当传入左值时,传值拷贝一次,再移动到vector,移动的开销远小于拷贝(仅指针转移,无原子操作)。 - 写法简洁无误导,调用者无需关心函数内部实现细节。
- 它能正确支持移动语义:当调用者传入右值(比如
避免
bar的const引用写法:- 写法有极强的误导性,看似要移动实际是拷贝,完全达不到预期的移动效果;
- 如果确实需要传引用实现移动,应把参数改为非const引用
std::shared_ptr<int>& ptr,但这种写法限制调用者只能传入可修改的左值,灵活性远不如传值写法。
内容的提问来源于stack exchange,提问作者xyf
相关产品推荐
相关产品推荐

