C++ vector中emplace_back与push_back搭配智能指针的差异及风险咨询
核心结论
“所有push_back都可以替换为emplace_back”的说法不成立,需要结合使用场景判断,智能指针场景有明确的边界约束,以下针对你的三个问题逐一解答:
1. 两种写法的差异
你给出的两行代码行为完全等价,没有可观测的性能差异:
// 写法1 myVector.emplace_back(std::make_shared<XXX>(x, y, z)); // 写法2 myVector.push_back(std::make_shared<XXX>(x, y, z));
std::make_shared<XXX>(x,y,z)会先生成一个std::shared_ptr<XXX>类型的右值临时对象。push_back接收这个右值后,会调用shared_ptr的移动构造函数将对象存入vector的预分配内存;emplace_back则是将这个右值作为入参,直接在vector的预分配内存调用shared_ptr的移动构造函数。两者都只会触发一次shared_ptr的移动操作,而shared_ptr的移动开销极低(仅拷贝两个指针、修改一次引用计数),所以实际运行表现没有区别。
2. emplace插入智能指针的内存泄漏问题
该说法仅针对特定错误写法成立,不是通用结论:
只有你直接将裸指针作为参数传入emplace_back时,才会存在泄漏风险,示例:
// 错误写法,存在内存泄漏风险 myVector.emplace_back(new XXX(x, y, z));
这种写法的执行顺序是:先执行new XXX获得裸指针,再尝试为vector扩容分配存储新元素的内存;如果内存分配失败抛出std::bad_alloc异常,此时裸指针还没有被包装成智能指针,没有RAII机制负责释放,就会出现内存泄漏。
但你示例中传入std::make_shared返回值的写法完全不会有这个问题:std::make_shared执行完成后就已经生成了持有资源的shared_ptr临时对象,哪怕后续vector内存分配失败,临时对象会自动触发析构释放持有的内存,不会发生泄漏。
3. 直接传构造参数的写法可行性与emplace的收益
myVector.emplace_back(x, y, z)的写法无法编译通过,智能指针场景下emplace_back也没有额外收益:
如果你的vector声明是std::vector<std::shared_ptr<XXX>> myVector,emplace_back的入参都会传递给std::shared_ptr<XXX>的构造函数,而shared_ptr不存在接收三个XXX构造参数的重载,所以编译会直接报错。
对于智能指针存入vector的场景,emplace_back没有额外的性能优势:本身shared_ptr的移动成本可以忽略,你也无法跳过shared_ptr的构造过程直接在vector内存中生成shared_ptr和其管理的对象,所以这种场景下两种写法都可以使用,只要规避直接传裸指针给emplace_back的错误写法即可。
内容的提问来源于stack exchange,提问作者Sébastien Bémelmans
相关产品推荐
相关产品推荐

