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

C++ 運算子多載完整指南:語法、最佳實踐、常見陷阱與現代演進

     編輯:VC 教學

前言

運算子多載讓使用者定義型別 (類別) 能像內建型別一樣自然運作:a + b、v[i]、++iter。核心目標是降低學習曲線、減少錯誤、提升可讀性。本文涵蓋語法規則、可/不可多載運算子、實作模式、決策準則與現代 C++ 演進。

一、基本語法形式

形式宣告範例適用運算子
成員函數Type operator@(Arg)一元運算子、= [] () ->* 必須為成員
友元/非成員函數Type operator@(Lhs, Rhs)二元運算子、左運算元非類別型別時

成員函數版 (一元/指派類)

class Complex {
    double re, im;
public:
    Complex& operator+=(const Complex& rhs) { // 複合指派回傳 *this
        re += rhs.re; im += rhs.im;
        return *this;
    }
    Complex operator-() const { return Complex(-re, -im); } // 一元負號
    Complex& operator++() { ++re; return *this; } // 前置++
    Complex operator++(int) { Complex tmp=*this; ++*this; return tmp; } // 後置++
};

非成員友元版 (二元對稱運算子)

class Complex { ... };

// 非成員,需友元存取私有成員
Complex operator+(Complex lhs, const Complex& rhs) { // lhs 以值傳遞支援移動優化
    lhs += rhs;
    return lhs; // NRVO/移動語意優化
}

bool operator==(const Complex& lhs, const Complex& rhs) {
    return lhs.re == rhs.re && lhs.im == rhs.im;
}

二、可多載與不可多載運算子

可多載 (大多數)不可多載
算術:+ - * / %. (成員存取)
位元:& | ^ ~ << >>?: (三元條件)
比較:== != < > <= >=:: (範圍解析)
邏輯:&& || !.* (成員指標存取)
指派:= += -= *= /= %= &= |= ^= <<= >>=sizeof (技術上屬運算子)
其他:[] () -> ->* new delete , (逗號)typeid, alignof, co_await 等

特殊限制

  • 至少一個運算元為使用者定義型別 (類別/列舉)
  • 不可改變運算子優先級、結合性、元數
  • operator&&/operator|| 失去短路求值特性,極不建議多載
  • operator, (逗號) 失去排序點保證,極不建議多載
  • operator& (取址) 多載會干擾標準庫容器,**極不建議**

三、核心運算子實作模式

1. 算術運算子:以複合指派為基礎

class Vec {
    double x, y;
public:
    Vec& operator+=(const Vec& rhs) { x+=rhs.x; y+=rhs.y; return *this; }
    friend Vec operator+(Vec lhs, const Vec& rhs) { lhs += rhs; return lhs; } // 值傳遞 lhs
    // -, *, / 同理
};

2. 陣列存取:回傳參考支援左值

class Matrix {
    std::vector data;
    size_t cols;
public:
    double& operator()(size_t r, size_t c) { return data[r*cols + c]; }
    const double& operator()(size_t r, size_t c) const { return data[r*cols + c]; }
    // 支援 m(1,2) = 3.14;
}

3. 智慧指標解參考

template<typename T>
class UniquePtr {
    T* ptr;
public:
    T& operator*() const { return *ptr; }
    T* operator->() const { return ptr; }
    // 支援 *p, p->member
};

4. 串流輸出

std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
    return os << "MyClass{val=" << obj.val << "}";
}

四、決策準則:何時該多載?

✅ 建議多載

  • 數學型別:Complex, Matrix, Vector, Rational, BigInt
  • 智慧指標/迭代器:* -> ++ -- == !=
  • 容器/範圍:[] () (可呼叫物件)
  • 字串類別:+ (串接), == != < (比較), << (輸出)
  • 強型別包裝器:比較、算術、串流

❌ 避免多載

  • 語意模糊:operator% 用於字串格式化 (Python 風格) 在 C++ 易誤解
  • 違反慣例:operator+ 執行非交換操作
  • 內建語意衝突:operator&&、operator||、operator,、operator&
  • 單純為了「寫起來酷」而非語意自然

五、常見陷阱

陷阱正確做法
回傳值而非參考 (T& operator+=)複合指派回傳 *this 參考
前置/後置 ++ 區分錯誤前置 T& operator++(),後置 T operator++(int)
operator[] 僅提供非 const 版提供 const/非 const 雙版本
operator== 非對稱/非傳遞嚴格滿足等價關係 (自反、對稱、傳遞)
隱式轉換導致意外多載解析關鍵建構子加 explicit

六、現代 C++ 演進 (C++11/14/17/20)

  • 移動語意:operator+(T lhs, const T& rhs) 值傳遞 lhs 啟用移動優化
  • 預設比較:C++20 operator<=> (三向比較) 自動產生 == != < <= > >=
  • Concepts 限制:template<Addable T> T operator+(T, T)
  • std::string_view:避免 const char* 隱式轉換
  • 結構化綁定:auto [x, y] = point; 依賴 get<0>() 或公有成員

七、經典面試題:為何 operator[] 回傳參考?

class Array {
    int data[100];
public:
    int& operator[](size_t i) { return data[i]; } // 左值:a[3] = 5;
    int operator[](size_t i) const { return data[i]; } // 右值:x = a[3];
};

回傳參考才能支援左值語意 (可被指派),回傳值僅支援右值讀取。

結語

運算子多載是「為使用者設計,非為實作者設計」。好的多載讓程式碼讀起來像領域語言 (DSL),壞的多載製造驚喜與 Bug。遵循慣例、保持語意直覺、善用現代特性 (移動語意、<=>、Concepts),讓類別自然融入 C++ 生態系。

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