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

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}))

  1. 构造临时对象:Move{10}触发构造函数调用1次。
  2. vector扩容并移动元素:vector初始容量为0,需要分配容量为1的内存,将临时对象移动到vector中,触发移动构造函数调用1次。
  3. 临时对象析构:临时对象完成移动后被销毁,触发析构函数调用1次。
  4. 程序结束时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。

第二步:放入第二个对象

  1. 构造临时对象:Move{20}触发构造函数第2次调用。
  2. vector扩容:当前容量1不足以容纳第二个元素,vector会将容量扩容至2(主流实现通常是翻倍扩容)。此时需要将旧内存中的Move{10}移动到新内存,触发移动构造函数第2次调用。
  3. 移动新元素:将临时对象Move{20}移动到新内存的第二个位置,触发移动构造函数第3次调用。
  4. 旧内存元素析构:旧内存中的原Move{10}对象被销毁(旧内存释放前清理),触发析构函数第2次调用。
  5. 临时对象析构:临时对象Move{20}完成移动后被销毁,触发析构函数第3次调用。
  6. 程序结束时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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:33:10