C++如何正确获取vector的shared_ptr并减少拷贝构造调用次数
问题根源
当前代码的额外拷贝开销主要来自两处:
- 核心开销:先在栈上创建局部
vector<A> myA,最终调用make_shared<vector<A>>(myA)时会触发vector的拷贝构造,把局部vector里存储的所有A对象全量复制到shared_ptr持有的堆内存vector中,元素量越大这部分开销越高。 - 次要开销:
push_back(A(10))会先构造临时A对象,再调用拷贝构造把临时对象复制进vector;另外vector插入过程中动态扩容时,也会触发已有元素的拷贝。
优化方案
以下方案可以把拷贝次数降到最低,完全消除全量元素拷贝的开销:
方案1:直接操作shared_ptr持有的堆vector(改动最小、收益最高)
从一开始就创建shared_ptr管理的堆上vector,所有插入操作直接针对该实例执行,从根源上消除最后一步的全量拷贝。配合提前预留容量、原地构造方法可以进一步消去冗余拷贝:shared_ptr<vector<A>> foo() { // 直接在堆上创建目标vector auto res = make_shared<vector<A>>(); // 提前按预估元素数预留容量,避免vector扩容触发的元素拷贝 res->reserve(2); // 业务场景替换为实际预估的元素总量 // 用emplace_back直接在vector内存上原地构造A对象,消除临时对象拷贝 res->emplace_back(10); res->emplace_back(20); return res; }该方案下不会出现任何vector层面的全量元素拷贝,A对象只会在最终存储的内存位置被构造一次。
方案2:用移动语义转移局部vector所有权(适合已有逻辑不便重构的场景)
如果因为业务逻辑需要,必须先在栈上操作局部vector,可以用std::move把局部vector转为右值,触发vector的移动构造。vector的移动构造是O(1)操作,仅复制内部数据指针、容量、大小几个元字段,不会复制任何存储的A对象。shared_ptr<vector<A>> foo() { vector<A> myA; myA.reserve(2); myA.emplace_back(10); myA.emplace_back(20); // 移动构造shared_ptr持有的vector,无元素级拷贝 return make_shared<vector<A>>(std::move(myA)); }注意调用
std::move(myA)后,局部变量myA会变为空vector,后续不能再对其做读写操作。
额外优化建议
- 若使用C++11及以上版本,可为类A实现轻量的移动构造,即使遇到vector扩容等需要转移元素的场景,开销也远小于拷贝构造
- 所有vector插入场景优先用
emplace_back代替push_back(临时对象),减少不必要的临时对象生成与拷贝 - 提前已知元素数量时必须调用
reserve预留容量,避免vector动态扩容时反复分配内存、迁移元素的开销
内容的提问来源于stack exchange,提问作者roy.atlas
相关产品推荐
相关产品推荐

