Effective C++ 条款11:在operator=中处理“自我赋值”

发布于:2025-07-31 ⋅ 阅读:(14) ⋅ 点赞:(0)

Effective C++ 条款11:在operator=中处理“自我赋值”


核心思想确保拷贝赋值操作符(operator=)在对象给自己赋值时(如 a = a;)能安全运行,避免资源泄漏、指针悬挂等问题。自我赋值可能通过别名、引用或指针间接发生,需显式处理。

⚠️ 1. 自我赋值的隐藏风险

以下代码存在致命风险

class Bitmap { /*...*/ };
class Widget {
    Bitmap* pb; // 指向堆内存的指针
public:
    Widget& operator=(const Widget& rhs) {
        delete pb;                // 释放当前资源
        pb = new Bitmap(*rhs.pb); // 复制rhs的资源
        return *this;
    }
};

问题
rhsthis 是同一对象时(*this = *this;):

  1. delete pb 销毁当前对象的资源
  2. new Bitmap(*rhs.pb) 访问已被销毁的 pb未定义行为

🛡️ 2. 解决方案
方法1:证同测试(Identity Test)
Widget& operator=(const Widget& rhs) {
    if (this == &rhs) return *this; // 关键:检查是否自我赋值
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

优点:阻止自我赋值的破坏性操作
缺点:若 new Bitmap 抛出异常,pb 指向已删除内存 → 异常不安全


方法2:精心安排语句顺序
Widget& operator=(const Widget& rhs) {
    Bitmap* pOrig = pb;        // 记住原指针
    pb = new Bitmap(*rhs.pb);  // 先复制新资源
    delete pOrig;              // 再释放旧资源
    return *this;
}

优点

  1. 自我赋值安全:复制新资源后再删除旧资源
  2. 异常安全:若 new 抛出异常,pb 保持原状

方法3:Copy and Swap(推荐)
class Widget {
    void swap(Widget& rhs); // 交换*this和rhs的所有成员
public:
    Widget& operator=(const Widget& rhs) {
        Widget temp(rhs);  // 创建副本(调用拷贝构造函数)
        swap(temp);        // 交换*this和副本
        return *this;      // 副本离开作用域自动销毁旧资源
    }
};

优化版(利用值传递)

Widget& operator=(Widget rhs) { // 值传递:自动调用拷贝构造
    swap(rhs);                 // 交换*this和副本
    return *this;              // 副本销毁旧资源
}

优势

  • 代码简洁
  • 天然处理自我赋值和异常安全

💡 关键原则总结

方案 自我赋值安全 异常安全 代码简洁性
证同测试 ★★☆
语句顺序调整 ★★★
Copy and Swap ★★★★
  1. 自我赋值的隐蔽性
    • 可能通过别名(a = b;ab 指向同一对象)或链式操作(arr[i] = arr[j];)发生。
  2. 异常安全的重要性
    • 若资源分配失败(如 new 抛出异常),对象必须保持有效状态。
  3. 终极解决方案:Copy and Swap
    • 结合拷贝构造函数和 swap,确保强异常安全和自我赋值安全。

示例代码:Copy and Swap 实现

class Widget {
    Bitmap* pb;
    void swap(Widget& rhs) noexcept {
        using std::swap;
        swap(pb, rhs.pb);
    }
public:
    Widget& operator=(Widget rhs) { // 按值传递
        swap(rhs);
        return *this;
    }
};

网站公告

今日签到

点亮在社区的每一天
去签到