vector::insert 在中间或开头插入时需搬移后续元素并逐个调用拷贝/移动构造函数,而 push_back 仅在尾部追加(除非扩容),故 insert 更慢;尤其对 std::string 等复杂对象,开销显著。
因为 insert 在中间或开头插入时,必须把插入点之后的所有元素往后搬移一位,而 push_back 只在尾部追加(除非触发扩容)。搬移是逐个调用拷贝/移动构造函数的,不是 memcpy —— 这意味着对象越复杂、拷贝开销越大,insert 越慢。
常见错误现象:vector<:string> v; for (int i = 0; i 这段代码实际是 O(n²) 时间复杂度,实测可能比预分配后反向填充慢百倍。
v.insert(v.begin(), x) 搬移全部现有元素v.insert(it, first, last))仍需一次性预留空间并搬移,不等于多次单元素插入只有插入位置恰好在逻辑末尾(即 it == v.end()),且容量足够时,insert 才等价于 push_back,不触发搬移。但注意:这不是语法保证,而是运行时条件判断的结果。
v.insert(v.end(), x):等效于 v.push_back(x),无搬移(容量够时)v.insert(v.begin() + v.size(), x):和上一条一样,但写法易错,不推荐v.reserve(N) 预分配后,再批量 insert 到末尾,可规避扩容干扰反例:v.insert(v.begin() + v.size() - 1, x) 仍会搬移最后一个元素,哪怕只差一个位置。
如果频繁在头部/中部插入,std::vector 本就不是合适选择。性能瓶颈不在写法,而在数据结构误用。
std::deque(支持 O(1) 头尾插入,但不支持指针稳定性)std::list + std::vector 索引(但 cache 不友好)std::vector 收集所有待插入元素,再用 std::inplace_merge 或排序合并(适合有序场景)std::vector 预分配 + 下标赋值,绕过所有 insert
搬移本身不可见,但可通过地址变化和迭代器失效间接验证。最直接的方式是观察插入前后元素地址是否变动:
std::vectorv = {1,2,3}; auto p = &v[0]; v.insert(v.begin(), 0); // 插入后 v[0] 地址很可能变了 assert(p != &v[0]); // 很可能触发
更实用的判断方式:

真正容易被忽略的是:即使没扩容,只要不是插在末尾,搬移就一定发生 —— 这和很多人凭直觉认为“只要 reserve 了就不会搬移”的理解相悖。