C++ vector调用push_back插入3元素触发6次拷贝构造的原因
现象原理
你观察到的6次拷贝构造,是std::vector动态扩容时拷贝旧元素、插入临时对象时拷贝入容器两个行为叠加的结果,和vector的内存管理逻辑直接相关。std::vector是基于连续内存存储的序列容器,初始状态下预留容量为0,当插入新元素时发现剩余容量不足,就会申请一块更大的连续新内存,把旧内存上存储的所有元素拷贝/移动到新内存,释放旧内存后再完成新元素的插入。另外你定义的vertex结构体没有自定义移动构造函数,push_back接收右值临时对象时不会触发移动语义,仍然会调用拷贝构造函数完成对象复制。
你当前使用的编译环境下vector扩容倍数为2(GCC、Clang均采用该策略,MSVC采用1.5倍扩容,最终拷贝计数会有差异),逐次插入的拷贝触发逻辑如下:
逐次插入的拷贝计数拆解
- 第一次执行
vertices.push_back(vertex(1, 2, 3))- 栈上生成第一个vertex临时对象
- vector初始容量为0,首次插入申请能容纳1个元素的内存
- 将临时对象拷贝构造到vector的新内存位置,触发第1次拷贝
- 执行完成后vector容量为1,存储元素1个
- 第二次执行
vertices.push_back(vertex(4, 5, 6))- 栈上生成第二个vertex临时对象
- 当前容量1不足以容纳2个元素,触发扩容:申请能容纳2个元素的新内存块
- 将旧内存中存储的1个vertex元素拷贝到新内存,触发第2次拷贝
- 释放旧内存块
- 将本次的临时对象拷贝构造到新内存的第二个位置,触发第3次拷贝
- 执行完成后vector容量为2,存储元素2个
- 第三次执行
vertices.push_back(vertex(7, 8, 9))- 栈上生成第三个vertex临时对象
- 当前容量2不足以容纳3个元素,再次触发扩容:申请能容纳4个元素的新内存块
- 将旧内存中存储的2个vertex元素依次拷贝到新内存,分别触发第4、第5次拷贝
- 释放旧内存块
- 将本次的临时对象拷贝构造到新内存的第三个位置,触发第6次拷贝
- 执行完成后vector容量为4,存储元素3个
三次插入累计触发6次拷贝,和你观察到的输出完全一致。
拷贝优化方案
你可以通过两种方式消除不必要的拷贝:
- 提前预分配容量:在插入元素前调用
vertices.reserve(3),直接申请足够存3个元素的内存,插入过程中不会触发扩容,可消除3次扩容带来的旧元素拷贝,总拷贝次数降到3次 - 改用原位构造方法:将
push_back()替换为emplace_back(),直接传入构造函数需要的参数(即写为vertices.emplace_back(1,2,3)),vector会直接在自身的内存位置调用vertex的构造函数,不需要先生成临时对象再拷贝,配合reserve(3)使用可以把拷贝构造的触发次数降到0。
附可直接运行的复现代码:
#include <iostream> #include <vector> struct vertex{ float x, y, z; vertex(float x, float y, float z) : x(x), y(y), z(z){ } vertex(const vertex& other) : x(other.x), y(other.y), z(other.z){ std::cout << "copied" << std::endl; } }; int main(){ std::vector<vertex> vertices; vertices.push_back(vertex(1, 2, 3)); vertices.push_back(vertex(4, 5, 6)); vertices.push_back(vertex(7, 8, 9)); }
内容的提问来源于stack exchange,提问作者maiev
相关产品推荐
相关产品推荐

