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

为何这段C++代码中拷贝构造函数被调用两次?

问题:std::vector push_back为何两次调用拷贝构造函数?

测试代码

#include <vector>
#include <iostream>

class A{
public:
    A() = default;

    A(const A& obj){
        std::cout << "Copy c-tor" << std::endl;
    }

    A(A&& obj){
        std::cout << "Move c-tor" << std::endl;
    }
};

int main(int argc, char *argv[]){
    std::vector<A> vA;

    std::cout << "Copy ctor will be invoked: " << std::endl;
    auto a = A();
    vA.push_back(a);

    std::cout << "Move ctor will be invoked: " << std::endl;
    vA.push_back(A());

    return 0;
}

实际输出

[amirreza@localhost copy]$ g++ copy.cpp 
[amirreza@localhost copy]$ ./a.out 
Copy ctor will be invoked: 
Copy c-tor
Move ctor will be invoked: 
Move c-tor
Copy c-tor

原因分析

这是std::vector的扩容机制和异常安全规则共同导致的:

  • 第一次push_back:vector初始容量通常为0,插入第一个元素时会分配内存,此时调用拷贝构造函数把a复制到vector中,符合预期。
  • 第二次push_back:此时vector的容量已经用完(仅能容纳1个元素),必须重新分配一块更大的内存(通常是原容量的2倍)。这时候需要把旧内存里的元素转移到新内存中。
  • 异常安全限制:你的类A的移动构造函数没有标记noexcept,vector为了保证异常安全——如果移动过程中抛出异常,旧内存里的元素还能完整保留——会放弃使用移动构造,转而调用拷贝构造函数来转移旧元素。这就是额外的那一次Copy c-tor的来源。

解决办法

  • 给A的移动构造函数加上noexcept标记,让vector可以安全地用移动构造来转移元素:
    A(A&& obj) noexcept {
        std::cout << "Move c-tor" << std::endl;
    }
    
  • 提前给vector预留足够容量,避免扩容:
    std::vector<A> vA;
    vA.reserve(2); // 预留2个元素的空间
    

内容的提问来源于stack exchange,提问作者Amir reza Riahi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 08:27:26