C++中的Copy-and-Swap惯用法:2025年指南及示例

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

什么是Copy-and-Swap惯用法,以及为什么它在2025年是必备技能



想象一下,您正在编写一个处理动态内存的类。一切进展顺利,直到需要重载赋值运算符。您编写operator=,检查自赋值,手动释放旧资源,分配新资源……然后在90%的情况下,您会引入一个导致内存泄漏或双重释放的错误。熟悉吗?



在C++中,有一个优雅的解决方案——copy-and-swap惯用法。这不仅仅是一个花哨的技巧,而是一项基础技术,它允许您编写安全、异常安全且简洁的代码。在2025年,当C++23已被广泛使用,而C++26标准即将到来之际,这个惯用法仍然是管理资源的类的黄金标准。



在本文中,我们将从头到尾剖析copy-and-swap惯用法:从它解决的问题开始,到实际的示例和2025年的最佳实践。准备好您的编译器——会有很多代码!



问题:为什么天真的operator=是有害的



让我们从一个管理动态数组的经典类示例开始。没有使用copy-and-swap惯用法,代码通常看起来像这样:



class DumbArray {public:    DumbArray(size_t size)         : size_(size), data_(new int[size]) {}        ~DumbArray() { delete[] data_; }        // 天真的赋值运算符    DumbArray& operator=(const DumbArray& other) {        if (this == &other) return *this; // 检查自赋值        delete[] data_;                    // 释放旧资源        size_ = other.size_;        data_ = new int[size_];           // 分配新资源        std::copy(other.data_, other.data_ + size_, data_);        return *this;    }    private:    size_t size_;    int* data_;};


乍一看,一切都很合理。但存在三个问题:

  • 缺乏基本异常保证。 如果new int[size_]抛出std::bad_alloc,对象将处于无效状态:data_将指向已释放的内存。
  • 逻辑重复。 数据复制已在拷贝构造函数中实现。为什么我们要手动编写相同的代码?
  • 自赋值检查。 它拖慢了代码速度,并且很容易被遗忘。此外,在95%的情况下不会发生自赋值,但检查仍然会执行。


这还不是全部。如果向类中添加其他资源(例如文件描述符或互斥锁),代码不仅会变得臃肿——还会变得危险。正是在这里,copy-and-swap惯用法登场了。



惯用法的核心:实现完美operator=的三个步骤



Copy-and-swap惯用法基于一个简单的想法:不要在赋值运算符中编写复制逻辑——使用拷贝构造函数和swap函数。算法如下:



  1. Copy(复制): 使用拷贝构造函数创建对象的临时副本。
  2. Swap(交换): 将当前对象的内容与临时副本交换。
  3. Destroy(销毁): 临时对象被销毁,释放旧资源。


在代码中,它看起来像这样:



class SmartArray {public:    SmartArray(size_t size = 0)         : size_(size), data_(size ? new int[size] : nullptr) {}        // 拷贝构造函数    SmartArray(const SmartArray& other)         : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) {        std::copy(other.data_, other.data_ + size_, data_);    }        // 析构函数    ~SmartArray() { delete[] data_; }        // 友元swap函数    friend void swap(SmartArray& first, SmartArray& second) noexcept {        using std::swap;        swap(first.size_, second.siz                

博客

书籍推荐