题目

编写程序,设计具有唯一标识符与生命周期控制台日志的 Item 类。演示并追踪当把 Item 对象存入 std::vector 时:分别使用 resize(3)、reserve(3)、push_back 以及扩容时,对象被默认构造、拷贝/移动构造与析构释放的完整生命周期链路;剖析移动构造若未添加 noexcept 关键字对 vector 扩容时性能退化的深远影响。

输入格式:无需外部输入。

输出格式:输出从容器初始化、扩容搬迁到容器销毁时每一个具体对象的操作流水日志。

数据范围与约定:C++ 强异常安全保证、移动语义与容器生命周期。

思路与算法

1. resize 引发的默认构造狂潮
vec.resize(N) 会立即在堆上连续触发 N 次默认构造函数,立即占据实际有效元素数量;而 reserve(N) 纯粹只开辟裸内存,一个对象都不构造。

2. noexcept 对 vector 扩容的关键作用
C++ 标准库承诺 vector 扩容具备“强异常安全保证”(Strong Exception Guarantee)。如果移动构造函数没有明确标记 noexcept,vector 在扩容搬迁旧元素时不敢使用移动,只能退化为慢速深拷贝构造;唯有声明了 noexcept,扩容才会安全启用高效移动。

3. 析构顺序对称性
容器析构时,按照从后向前的逆序依次调用每个元素的析构函数,随后一次性释放整块堆空间。

完整程序

#include <iostream>
#include <vector>

class Item {
private:
    int id_;
public:
    Item() : id_(0) {
        std::cout << "  [默认构造] Item id = " << id_ << "\n";
    }

    explicit Item(int id) : id_(id) {
        std::cout << "  [有参构造] Item id = " << id_ << "\n";
    }

    Item(const Item& o) : id_(o.id_) {
        std::cout << "  [拷贝构造] Item id = " << id_ << "\n";
    }

    Item(Item&& o) noexcept : id_(o.id_) {
        std::cout << "  [移动构造 (noexcept)] Item id = " << id_ << "\n";
        o.id_ = -1;
    }

    ~Item() {
        std::cout << "  [析构函数] Item id = " << id_ << "\n";
    }
};

int main() {
    std::cout << "=== 1. 测试 resize(2) ===\n";
    {
        std::vector<Item> v;
        v.resize(2); // 连续就地默认构造 2 个对象
    } // 离开作用域析构
    std::cout << "resize 作用域结束\n\n";

    std::cout << "=== 2. 测试 reserve + push_back 扩容迁移 ===\n";
    {
        std::vector<Item> v2;
        std::cout << "插入 Item(1):\n";
        v2.push_back(Item(1)); // capacity 0 -> 1

        std::cout << "\n插入 Item(2) (触发扩容 1->2):\n";
        v2.push_back(Item(2)); // 旧元素触发 noexcept 移动构造
        std::cout << "插入完毕,准备离开作用域...\n";
    }
    std::cout << "所有对象均已安全析构释放\n";

    return 0;
}

运行示例

输出:

=== 1. 测试 resize(2) ===
  [默认构造] Item id = 0
  [默认构造] Item id = 0
  [析构函数] Item id = 0
  [析构函数] Item id = 0
resize 作用域结束

=== 2. 测试 reserve + push_back 扩容迁移 ===
插入 Item(1):
  [有参构造] Item id = 1
  [移动构造 (noexcept)] Item id = 1
  [析构函数] Item id = -1

插入 Item(2) (触发扩容 1->2):
  [有参构造] Item id = 2
  [移动构造 (noexcept)] Item id = 2
  [移动构造 (noexcept)] Item id = 1
  [析构函数] Item id = -1
  [析构函数] Item id = -1
插入完毕,准备离开作用域...
  [析构函数] Item id = 1
  [析构函数] Item id = 2
所有对象均已安全析构释放