首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

Effective C++ 條款 11:在 operator= 中處理自我賦值

     編輯:C++ 入門

什麼是自我賦值?

自我賦值發生在對象被賦值給自己時,例如:

Widget w;
w = w; // 顯式自我賦值
a[i] = a[j]; // 若 i == j,隱式自我賦值
*px = *py;   // 若 px == py,隱式自我賦值

核心觀點

實現「異常安全」的 operator= 往往自動獲得「自我賦值安全」。現代 C++ 實踐傾向於專注於異常安全性(強異常安全保證:操作失敗時對象狀態不變),而非顯式檢測自我賦值。

經典失敗案例

// 危險:既非異常安全也非自我賦值安全
Widget& Widget::operator=(const Widget& rhs) {
    delete pImpl_;        // 1. 釋放舊資源
    pImpl_ = new Impl(*rhs.pImpl_); // 2. 分配新資源(可能拋出異常)
    return *this;
}
// 若 rhs 為 *this:步驟 1 刪除了 pImpl_,步驟 2 嘗試從已刪除內存拷貝 → 未定義行為
// 若步驟 2 拋出異常:pImpl_ 已被刪除,對象處於無效狀態 → 違反強異常安全

推薦方案:Copy-and-Swap 惯用法

版本一:傳統形式


class Widget {
public:
    Widget& operator=(const Widget& rhs) {
        Widget temp(rhs);    // 1. 複製構造(可能拋出異常,但原對象未修改)
        swap(temp);          // 2. 交換內部狀態(不拋出異常)
        return *this;        // 3. temp 析構時釋放舊資源
    }
    void swap(Widget& rhs) noexcept { /* 交換所有成員 */ }
};

版本二:傳值參數形式(C++11 移動語義優化)


Widget& Widget::operator=(Widget rhs) { // 參數按值傳遞,調用者隱式構造副本
    swap(rhs);                        // 交換狀態
    return *this;                     // rhs 析構釋放舊資源
}

優勢:將拷貝操作移至參數構造階段,編譯器可優化(如拷貝消除、移動構造),代碼更簡潔。

為什麼 Copy-and-Swap 同時解決兩個問題?

場景傳統實現Copy-and-Swap
自我賦值需顯式檢測 if (this == &rhs)無需檢測:複製構造創建獨立副本,交換後原對象獲取副本資源,舊資源隨 temp 銷毀
異常安全(強保證)難以保證:資源釋放後再分配可能拋出異常天然滿足:拷貝構造失敗時原對象未修改;交換不拋出異常
代碼復雜度高:需手動管理資源釋放/分配順序低:委託給複製構造函數和 swap

前置條件

  • 類提供 swap 成員函數(或非成員 swap),且標記 noexcept
  • 複製構造函數正確實現(深拷貝資源)
  • 析構函數正確釋放資源

總結

不再手寫自我賦值檢測。
使用 Copy-and-Swap 惯用法實現 operator=,
同時獲得 自我賦值安全 與 強異常安全保證,
代碼更簡潔、更健壯、更易維護。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved