首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C++拷貝賦值運算符中自賦值檢查的必要性與實現模式

     編輯:程式設計綜合問答

問題背景

重載operator=時,為什麼要在開頭檢查if (this == &rhs) return *this;?這不是多此一舉嗎?

自賦值發生場景

MyClass obj;
obj = obj;                    // 顯式自賦值
obj = std::move(obj);         // 移動自賦值 (C++11)
container[i] = container[j];  // 容器元素複製時 i==j
*ptr1 = *ptr2;                // 指針指向同一對象

不檢查的災難性後果

典型資源管理類實現:

MyClass& operator=(const MyClass& rhs) {
    delete[] data_;           // 1. 釋放舊資源
    data_ = new char[len_];   // 2. 分配新內存
    std::copy(rhs.data_, rhs.data_ + len_, data_); // 3. 複製數據
    return *this;
}

若this == &rhs:

  1. delete[] data_釋放了rhs.data_指向的內存(因為是同一塊)
  2. new char[len_]可能分配到不同地址
  3. std::copy從已釋放的內存讀取 → 未定義行為(崩潰、數據損壞、靜默錯誤)

標準檢查模式

// 模式一:指針比較(最常用)
MyClass& operator=(const MyClass& rhs) {
    if (this == &rhs) return *this; // 自賦值保護
    // ... 正常複製邏輯
    return *this;
}

// 模式二:Copy-and-Swap 惯用法(強異常安全、天然防自賦值)
MyClass& operator=(MyClass rhs) { // 參數按值傳遞觸發拷貝構造
    swap(*this, rhs);             // 交換資源
    return *this;                 // rhs 析構時釋放舊資源
}

Copy-and-Swap 優勢

  • 自動處理自賦值:參數按值傳遞已完成拷貝,交換無風險
  • 強異常安全:拷貝構造失敗不修改原對象
  • 代碼復用:利用拷貝構造函數與swap
  • 統一資源管理:析構函數統一釋放

移動賦值運算符 (C++11)

MyClass& operator=(MyClass&& rhs) noexcept {
    if (this == &rhs) return *this; // 移動自賦值同樣需檢查
    delete[] data_;
    data_ = rhs.data_;
    len_ = rhs.len_;
    rhs.data_ = nullptr;
    rhs.len_ = 0;
    return *this;
}

現代C++最佳實踐

  1. 優先使用Copy-and-Swap:簡潔、安全、無需顯式自賦值檢查
  2. 若手寫拷貝賦值:必須在開頭加if (this == &rhs) return *this;
  3. Rule of Five:析構、拷貝構造、拷貝賦值、移動構造、移動賦值五者成雙成對
  4. 優先用智能指針/容器:std::unique_ptr、std::vector自動處理資源管理,編譯器生成的特殊成員函數通常足夠

總結

自賦值檢查不是「多此一舉」,而是資源管理類的生存底線。Copy-and-Swap惯用法以優雅方式解決此問題並提供強異常安全。現代C++應盡量避免手寫資源管理,讓標準庫組件承擔複雜度。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved