運算符重載讓用戶定義類型 (字符串、日期、複數、矩陣) 擁有內建類型般的直觀語法。但 `operator+` 的正確實現涉及**對稱性、返回值類型、拷貝開銷、標準庫兼容性**等多重權衡。本文建立決策框架,從成員 vs 非成員、返回值策略、C++11 移動語義優化三個維度給出工程級最佳實踐。
原則:重載運算符**盡可能模仿內建行為**,違背直覺即為糟糕設計。
| 形式 | 簽名 | 對稱性 | 隱式轉換 | 標準庫兼容 | 推薦度 |
|---|---|---|---|---|---|
| 成員 | `T T::operator+(const T& rhs)` | **破壞** (左操作數 `*this` 隱式 `const T*`,右操作數 `const T&`) | 僅右操作數觸發 | **差** (算法要求 `a+b` 與 `b+a` 同樣合法) | ❌ 非對稱運算符 (`+=`, `[]`, `()`) |
| 非成員 (友元) | `T operator+(const T& lhs, const T& rhs)` | **完美** (雙邊平等) | 雙邊均觸發 | **優** (滿足 `Regular` / `Addable` 概念) | ✅ 對稱二元運算符 (`+`, `-`, `==`, `<`) |
class String {
// ...
friend String operator+(const String& lhs, const String& rhs); // 聲明友元
};
// 實現
String operator+(const String& lhs, const String& rhs) {
String res;
res.reserve(lhs.size() + rhs.size());
res.append(lhs).append(rhs);
return res; // C++17 RVO / C++11 移動構造
}
| 返回類型 | 優點 | 致命缺陷 | 結論 |
|---|---|---|---|
| `T` (按值) | 語義正確 (右值)、支援鏈式 `a+b+c`、RVO/移動消除拷貝 | 大對象理論拷貝開銷 (現代編譯器優化極好) | ✅ **首選** |
| `T&` | 無拷貝 | 必須返回靜態/成員變量 → 線程不安全、連續調用覆蓋、鏈式失效 (`a+b+c` 編譯錯誤) | ❌ 禁用 |
| `T*` | 無拷貝 | 破壞類型一致性、調用者負責 `delete` → 內存洩漏高風險、鏈式不可行 | ❌ 禁用 |
| `const T&` | 無拷貝 | 同 `T&` 靜態變量問題、綁定臨時量壽命延長但鏈式修改語義錯誤 | ❌ 禁用 |
傳統 C++98:每次 `+` 產生臨時對象,拷貝構造下一輪參數,O(n²) 字符拷貝。
C++11 移動構造:臨時對象 (右值) 移動構造下一輪 `lhs`,**指針竊取 O(1)**,總體 O(n)。
// 支持左值+右值、右值+左值、右值+右值 避免不必要拷貝
String operator+(String&& lhs, const String& rhs) { // lhs 是臨時量
lhs.append(rhs); // 直接在 lhs 緩衝區追加
return std::move(lhs);
}
String operator+(const String& lhs, String&& rhs) {
rhs.insert(0, lhs); // 或預留空間後移動
return std::move(rhs);
}
String operator+(String&& lhs, String&& rhs) {
lhs.append(rhs);
return std::move(lhs);
}
效果:表達式鏈中**只有第一個左值參與拷貝**,其餘均移動/原地修改,極大提升性能。
class String {
public:
String& operator+=(const String& rhs) { // 核心邏輯
append(rhs); return *this;
}
friend String operator+(String lhs, const String& rhs) { // 參數按值傳遞 (複製或移動)
lhs += rhs; // 復用核心
return lhs; // RVO/移動返回
}
};
優勢:
`operator+` 的正確設計公式:
// 1. 核心修改邏輯封裝在 +=
T& T::operator+=(const T& rhs) { /* ... */ return *this; }
// 2. 非成員 + 以按值參數 + 復用 +=
T operator+(T lhs, const T& rhs) { lhs += rhs; return lhs; }
// 3. C++11 移動優化:為右值參數提供 && 重載原地修改
T operator+(T&& lhs, const T& rhs) { lhs += rhs; return std::move(lhs); }
// ... 其他 && 組合
遵循**對稱性、值語義、移動優化、核心邏輯復用**四原則,你的類型將擁有媲美標準庫 `std::string`/`std::vector` 的運算符體驗與性能表現。