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

Bjarne《C++之旅》Vector示例的移动赋值正确性及验证方法

C++ Vector类移动赋值运算符验证与移动操作确认方法

一、移动赋值运算符的正确实现示例

假设书中的Vector类基础结构如下,对应的移动赋值运算符可以这么写:

class Vector {
private:
    double* elem;
    int sz;
public:
    // 书中给出的移动构造函数
    Vector(Vector&& a) noexcept : elem{a.elem}, sz{a.sz} {
        a.elem = nullptr;
        a.sz = 0;
    }

    // 移动赋值运算符实现
    Vector& operator=(Vector&& a) noexcept {
        if (this != &a) { // 防自赋值,虽然场景少见但严谨性拉满
            delete[] elem; // 先释放当前对象的已有资源
            elem = a.elem; // 直接"窃取"源对象的内存指针
            sz = a.sz;
            // 把源对象置成安全可析构的空状态,避免重复释放内存
            a.elem = nullptr;
            a.sz = 0;
        }
        return *this;
    }

    // 其他必要成员(拷贝构造、拷贝赋值、析构函数)
    Vector(const Vector&);
    Vector& operator=(const Vector&);
    ~Vector() { delete[] elem; }
};

核心要点:

  • 必须加noexcept:标准库容器依赖这个标识启用移动优化,而且移动操作本身不应该抛出异常
  • 处理自赋值:避免自己给自己赋值时出现资源释放错误
  • 源对象置空:移动后源对象要处于“能安全调用析构函数”的状态,不能让它还握着原来的内存指针

二、区分移动/拷贝构造函数执行的方法

1. 加日志打印最直接

在拷贝和移动构造函数里加输出语句,运行时看控制台输出就一目了然:

Vector(const Vector& a) {
    std::cout << "触发拷贝构造函数\n";
    sz = a.sz;
    elem = new double[sz];
    std::copy(a.elem, a.elem + sz, elem);
}

Vector(Vector&& a) noexcept : elem{a.elem}, sz{a.sz} {
    std::cout << "触发移动构造函数\n";
    a.elem = nullptr;
    a.sz = 0;
}

比如执行Vector v2 = std::move(v1);,如果打印“触发移动构造函数”,就说明走的是移动逻辑。

2. 检查源对象状态

移动操作后,源对象会被我们置为空(指针nullptr、大小0),而拷贝操作不会改变源对象。可以直接判断:

Vector v1(100);
Vector v2 = std::move(v1);
if (v1.elem == nullptr && v1.sz == 0) {
    std::cout << "确实执行了移动操作\n";
}

要是拷贝的话,v1的elem还是指向原来的内存块,sz也不会变。

3. 用编译器工具辅助

比如GCC/Clang开-Wdeprecated-copy选项,当代码本该走移动却意外触发拷贝时,编译器会给警告。或者加-fno-elide-constructors关闭返回值优化(RVO),这样能看到原本被优化掉的移动构造调用(不过RVO是合法优化,实际项目里不用特意关)。

4. 性能对比(大数据量场景)

如果Vector存了百万级别的数据,拷贝要花时间分配内存+复制数据,移动只是几个指针赋值,耗时差非常大。用std::chrono计时就能看出来:

#include <chrono>

// 测试拷贝
Vector big_v(1000000);
auto start = std::chrono::high_resolution_clock::now();
Vector v_copy = big_v;
auto end = std::chrono::high_resolution_clock::now();
std::cout << "拷贝耗时:" << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms\n";

// 测试移动
start = std::chrono::high_resolution_clock::now();
Vector v_move = std::move(big_v);
end = std::chrono::high_resolution_clock::now();
std::cout << "移动耗时:" << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms\n";

移动的耗时基本是0ms或者个位数,拷贝则会有明显的耗时。

内容的提问来源于stack exchange,提问作者heretoinfinity

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 09:05:36