使用者定義型別(字串、日期、複數、檔案等)常需重載二元 + 實作串接、附加或合併。雖 C++ 提供多種實作方式,但易產生設計誤解,影響正確性、效能與標準庫相容性。本文分析內建 + 特徵,並介紹正確重載策略。
a + b == b + a。重載時應盡量模仿上述語意。
成員函式寫法:
class String {
public:
bool operator==(const String& s); // 隱含 this 指標,非對稱
};
缺點:兩參數型別不同(const String* const vs const String&),破壞對稱性,導致部分 STL 演算法/容器無法正確處理。
非成員函式寫法(推薦):
String operator+(const String& s1, const String& s2); // 兩參數型別相同
class String {
public:
friend String operator+(const String&, const String&);
};
優點:對稱、符合內建語意、完美配合 STL。
回傳指標 String*:違反「回傳型別與運算元相同」原則;鏈式運算 s1+s2+s3 無法編譯;需動態配置記憶體,易造成記憶體洩漏。
回傳參考 String&:避免動態配置,但必須回傳有效物件。常見做法回傳區域靜態物件,卻引發多執行緒競爭、狀態殘留等嚴重問題,不可取。
回傳實值 String(推薦):雖有複製成本,但現代編譯器支援 RVO/NRVO(回傳值優化)與 C++11 移動語意,實際開銷極小。最安全、最簡單、符合直覺,且支援鏈式運算。
class String {
char* data;
size_t len;
public:
String(const char* s = "");
String(const String&);
String(String&&) noexcept;
~String();
String& operator=(const String&);
String& operator=(String&&) noexcept;
friend String operator+(const String& lhs, const String& rhs) {
String res;
res.len = lhs.len + rhs.len;
res.data = new char[res.len + 1];
memcpy(res.data, lhs.data, lhs.len);
memcpy(res.data + lhs.len, rhs.data, rhs.len + 1);
return res; // RVO/移動語意消除複製
}
};
friend 非成員函式實作 operator+,確保對稱性。operator+= 作為基礎,operator+ 可委託給 += 實作,減少代碼重複。