题目

编写程序,手写包含大块堆内存的类 DynamicBuffer。完整实现现代 C++ 移动语义的两大核心:移动构造函数(T(T&& other) noexcept)与移动赋值运算符(T& operator=(T&& other) noexcept)。展示移动语义纯粹靠“偷取/接管”(Stealing)源对象的堆内存裸指针,并将源对象的指针安全置为空(other.data_ = nullptr),从而以 O(1) 常数时间彻底取代 O(N) 深拷贝的高性能奇迹。

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

输出格式:输出拷贝构造与移动构造在内存分配、耗时与原对象指针置空上的比对日志。

数据范围与约定:移动语义实现规范、noexcept 异常保证、资源偷取与源对象重置。

思路与算法

1. 移动的底层本质
深拷贝是开辟新内存再复制字节;移动语义直接将源对象的裸指针赋值给自己(this->ptr = other.ptr),然后将 other.ptr = nullptr。没有发生任何堆内存分配与数据拷贝!

2. 源对象必须处于合法有效析构状态
移动完成后,源对象并不会立即消失,它后续还会执行析构函数。因此必须将其内部指针置为 nullptr(delete nullptr 是安全的无操作),否则源对象析构时会误把刚刚偷过来的堆内存销毁掉!

3. noexcept 为什么必不可少
如前所述,只有加上 noexcept,标准库容器(如 vector 扩容)才会放心地启用移动语义。

完整程序

#include <iostream>
#include <utility>

class DynamicBuffer {
private:
    int* buffer_;
    size_t size_;

public:
    explicit DynamicBuffer(size_t size) : size_(size), buffer_(new int[size]{0}) {
        std::cout << "  [有参构造] 申请堆内存成功 (大小: " << size_ << ", 非空: " << (buffer_ != nullptr) << ")\n";
    }

    ~DynamicBuffer() {
        std::cout << "  [析构] 释放堆内存, 指针是否为空: " << (buffer_ == nullptr ? "是" : "否") << "\n";
        delete[] buffer_;
    }

    // 1. 移动构造函数:直接偷取指针并置空对方
    DynamicBuffer(DynamicBuffer&& other) noexcept 
        : buffer_(other.buffer_), size_(other.size_) {
        // 关键:将对方重置为空状态,防止对方析构时误删这块内存
        other.buffer_ = nullptr;
        other.size_ = 0;
        std::cout << "  [移动构造] 零拷贝偷取堆指针, 接管后非空: " << (buffer_ != nullptr) << "\n";
    }

    // 2. 移动赋值运算符
    DynamicBuffer& operator=(DynamicBuffer&& other) noexcept {
        std::cout << "  [移动赋值] 触发资源交接\n";
        if (this != &other) {
            delete[] buffer_; // 释放自身已有旧内存

            buffer_ = other.buffer_; // 偷取资源
            size_ = other.size_;

            other.buffer_ = nullptr; // 源对象指针置空
            other.size_ = 0;
        }
        return *this;
    }

    const int* getBuffer() const { return buffer_; }
};

int main() {
    std::cout << "--- 1. 实例化 bufferA ---\n";
    DynamicBuffer bufA(1000000); // 占用百万整型大内存

    std::cout << "\n--- 2. 触发移动构造: bufferB(std::move(bufferA)) ---\n";
    DynamicBuffer bufB(std::move(bufA)); // 瞬间常数级转移所有权

    std::cout << "移动后 bufA 是否为空: " << (bufA.getBuffer() == nullptr) << " (已安全置空为 nullptr)\n";
    std::cout << "移动后 bufB 是否非空: " << (bufB.getBuffer() != nullptr) << " (原内存完全保留)\n";

    std::cout << "\n--- 3. 作用域结束,各自独立析构 ---\n";
    return 0;
}

运行示例

输出:

--- 1. 实例化 bufferA ---
  [有参构造] 申请堆内存成功 (大小: 1000000, 非空: 1)

--- 2. 触发移动构造: bufferB(std::move(bufferA)) ---
  [移动构造] 零拷贝偷取堆指针, 接管后非空: 1
移动后 bufA 是否为空: 1 (已安全置空为 nullptr)
移动后 bufB 是否非空: 1 (原内存完全保留)

--- 3. 作用域结束,各自独立析构 ---
  [析构] 释放堆内存, 指针是否为空: 否
  [析构] 释放堆内存, 指针是否为空: 是