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

shared_ptr插入vector:传值/const引用+移动的用法对比与疑问

std::shared_ptr插入std::vector的两种用法分析

核心行为与疑问解答

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)时:

    1. 传值过程会拷贝sp,此时sp的引用计数从1变为2(原sp + 函数内的ptr);
    2. 函数内移动ptr到vector,ptr被掏空(引用计数减1),最终引用计数回到2(原sp + vector里的新元素);
    3. 所以main里的sp调用use_count()返回2。
  • 调用a.bar(sp)时:

    1. 传const引用不会增加引用计数,此时sp的计数还是1;
    2. 因为无法移动,只能拷贝ptr到vector,引用计数增加到2(原sp + vector里的新元素);
    3. 所以main里的sp调用use_count()返回2。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 22:50:40