为何这段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
相关产品推荐
相关产品推荐

