內建型別的 + 具備三大特徵:二元對稱(兩操作數型別相同)、可交換(a+b == b+a)、返回右值且型別與操作數一致(int x = 4+2 返回 int 右值)。為使用者定義型別重載 + 時,應盡量遵循這些特徵,確保與 STL 演算法、標準庫組件良好互操作。
class String {
public:
bool operator==(const String& s); // 隱含 this 指標,參數型別不對稱
};
成員 operator+ 的隱含參數為 const String* const,顯式參數為 const String&,破壞對稱性。導致 STL 容器/演算法(如 std::find、std::sort)無法正確處理該型別。
class String {
friend String operator+(const String& lhs, const String& rhs);
};
String operator+(const String& lhs, const String& rhs) {
String result = lhs;
result += rhs; // 復用 operator+=
return result; // 返回右值
}
friend 以存取私有成員operator+= 避免代碼重複| 方案 | 問題 |
|---|---|
返回 String* | 破壞「返回型別=操作數型別」規則;鏈式表達式 s1+s2+s3 無法工作;需動態配置記憶體,易洩漏 |
返回 String& | 必須綁定有效物件;若返回區域靜態變數,多執行緒下資料競爭;每次調用需清理狀態,效能差 |
String operator+(const String& lhs, const String& rhs) {
String temp = lhs;
temp += rhs;
return temp; // 編譯器優化消除拷貝 (RVO)
}
現代編譯器(C++11 起)保證複製省略,C++17 強制 RVO。即使舊標準,String 實作移動語意後,返回值開銷極低。這是最安全、最簡單、效能最佳的方案。
class String {
char* data_;
size_t len_;
public:
String(const char* s = "") { /* ... */ }
String(const String& other) { /* 深拷貝 */ }
String(String&& other) noexcept { /* 移動構造 */ }
~String() { delete[] data_; }
String& operator+=(const String& rhs) { /* 追加實作 */ return *this; }
String& operator=(String&& rhs) noexcept { /* 移動賦值 */ return *this; }
friend String operator+(const String& lhs, const String& rhs) {
String result = lhs; // 複製左操作數
result += rhs; // 就地修改
return result; // RVO/移動返回
}
};
"hello" + str)operator+=:單一邏輯來源,易維護遵循此策略,重載的 operator+ 將具備與內建型別一致的行為、優異效能、且完美融入 C++ 標準生態系統。