C++移动构造函数调用逻辑解析:vector扩容引发的次数疑问
std::vector中对象构造/移动构造/析构调用次数解析
问题现象
给定如下C++代码:
#include <iostream> #include <vector> class Move { private: int* data; public: Move(int d) { data = new int; *data = d; std::cout << "Constructor is called for " << d << std::endl; } Move(const Move& source) : Move{*source.data} { std::cout << "Copy constructor is called - " << "Deep copy for " << *source.data << std::endl; } Move(Move&& source) noexcept : data{ source.data } { std::cout << "Move constructor for " << *source.data << std::endl; source.data = nullptr; } ~Move() { if (data != nullptr) { std::cout << "Destructor is called for " << *data << std::endl; } else { std::cout << "Destructor is called for " << "nullptr" << std::endl; } delete data; } }; int main() { std::vector<Move> vec; vec.push_back(Move{ 10 }); vec.push_back(Move{ 20 }); return 0; }
运行后观察到:
- 仅放入1个对象时:构造函数调用1次、移动构造函数调用1次、析构函数调用2次
- 放入2个对象时:构造函数调用2次、移动构造函数调用3次、析构函数调用5次
以下是对调用次数差异的详细解释:
核心原因:std::vector的动态扩容机制
std::vector是动态数组,初始容量为0。当新元素加入时,如果当前容量不足以容纳新元素,vector会重新分配一块更大的内存,将旧内存中的所有元素移动到新内存,再释放旧内存。这一过程会额外触发移动构造和析构调用。
情况1:仅放入1个对象(vec.push_back(Move{10}))
- 构造临时对象:
Move{10}触发构造函数调用1次。 - vector扩容并移动元素:vector初始容量为0,需要分配容量为1的内存,将临时对象移动到vector中,触发移动构造函数调用1次。
- 临时对象析构:临时对象完成移动后被销毁,触发析构函数调用1次。
- 程序结束时vector元素析构:vector中的
Move{10}对象被销毁,触发析构函数第2次调用。
总次数:构造1次、移动构造1次、析构2次,与观察一致。
情况2:放入2个对象(vec.push_back(Move{10}) + vec.push_back(Move{20}))
第一步:放入第一个对象
过程和情况1完全一致:构造1次、移动构造1次、临时对象析构1次。此时vector的容量为1,大小为1。
第二步:放入第二个对象
- 构造临时对象:
Move{20}触发构造函数第2次调用。 - vector扩容:当前容量1不足以容纳第二个元素,vector会将容量扩容至2(主流实现通常是翻倍扩容)。此时需要将旧内存中的
Move{10}移动到新内存,触发移动构造函数第2次调用。 - 移动新元素:将临时对象
Move{20}移动到新内存的第二个位置,触发移动构造函数第3次调用。 - 旧内存元素析构:旧内存中的原
Move{10}对象被销毁(旧内存释放前清理),触发析构函数第2次调用。 - 临时对象析构:临时对象
Move{20}完成移动后被销毁,触发析构函数第3次调用。 - 程序结束时vector元素析构:vector中的
Move{10}和Move{20}依次被销毁,触发析构函数第4、5次调用。
总次数:构造2次、移动构造3次、析构5次,与观察一致。
补充说明
- 如果提前用
vec.reserve(2)预分配足够容量,放入2个对象时不会触发扩容,此时移动构造只会调用2次,析构次数为4次(2个临时对象+vector中2个元素)。 - 代码中
Move类的移动构造函数被标记为noexcept,这是vector选择移动而非拷贝元素的前提——如果移动构造函数不是noexcept,扩容时会调用拷贝构造函数而非移动构造函数。
内容的提问来源于stack exchange,提问作者alkendev
相关产品推荐
相关产品推荐

