運算子重載


在一些情況下,會想將兩個物件進行 +-*/ 運算,例如在定義了有理數類別之後,若能透過 +-*/ 之類的運算來處理,程式碼撰寫上會比較直覺,在 C++ 中,可以透過重載運算子來達到目的。

運算子重載是函式重載的延伸應用,定義類別時可以指定重載哪個運算子,實作對應的運算,運算子重載的語法如下:

傳回型態 類別名稱::operator#(參數列) {
    // 實作重載內容
}

其中 # 指明要重載哪個運算子,例如重載一個 + 運算子,# 處就替換為 +

如果要重載 ++-- 運算子,必須注意前置與後置,這是使用一個 int 參數來區別:

傳回型態 operator++();    // 前置,例如 ++x
傳回型態 operator++(int); // 後置,例如 x++
傳回型態 operator--();    // 前置,例如 --x
傳回型態 operator--(int); // 後置,例如 x--

後置的 int 會傳入 0,實際上沒有作用,只是用來識別前置或後置,通常在重載 ++-- 運算子時,前置與後置都要重載。

底下範例定義了有理數 Rational 類別,並重載了一些運算子:

#include <iostream>
#include <string>
using namespace std;

class Rational {
    int numer;
    int denom;

public:
    Rational(int numer, int denom) : numer(numer), denom(denom) {}
    Rational operator+(const Rational&);
    Rational operator-(const Rational&);
    Rational operator*(const Rational&);
    Rational operator/(const Rational&);
    Rational& operator++();
    Rational& operator--();
    Rational operator++(int);
    Rational operator--(int);
    string to_string() const;
};

Rational Rational::operator+(const Rational &that) { 
    return Rational(
        this->numer * that.denom + that.numer * this->denom,
        this->denom * that.denom
    ); 
} 

Rational Rational::operator-(const Rational &that) { 
    return Rational(
        this->numer * that.denom - that.numer * this->denom,
        this->denom * that.denom
    ); 
} 

Rational Rational::operator*(const Rational &that) { 
    return Rational(
        this->numer * that.numer,
        this->denom * that.denom
    ); 
} 

Rational Rational::operator/(const Rational &that) { 
    return Rational(
        this->numer * that.denom,
        this->denom * that.numer
    ); 
} 

Rational& Rational::operator++() {
    this->numer = this->numer + this->denom;
    return (*this);
}

Rational& Rational::operator--() {
    this->numer = this->numer - this->denom;
    return (*this);
}

Rational Rational::operator++(int) {
    Rational r = (*this);
    this->numer = this->numer + this->denom;
    return r;
}

Rational Rational::operator--(int) {
    Rational r = (*this);
    this->numer = this->numer - this->denom;
    return r;
}

string Rational::to_string() const { 
    return std::to_string(this->numer) + "/" + std::to_string(this->denom);
} 

int main() {
    Rational a(1, 2);
    Rational b(2, 3);

    cout << (a + b).to_string() << endl;
    cout << (a - b).to_string() << endl;
    cout << (a * b).to_string() << endl;
    cout << (a / b).to_string() << endl;
    cout << (++a).to_string() << endl;
    cout << (--a).to_string() << endl;
    cout << (b++).to_string() << endl;
    cout << (b--).to_string() << endl;

    return 0;
}

有些運算子重載可以實作為類別成員函式,也可以實作為一般函式,涉及 private 值域存取的,通常會實作為成員函式,然而,若運算子涉及不同型態的運算,例如 Rational 加法運算的左或右運算元,可以是 int 整數的話,運算子就得定義為非成員函式,例如:

#include <iostream>
#include <string>
using namespace std;

class Rational {
    int numer;
    int denom;

public:
    Rational(int numer, int denom) : numer(numer), denom(denom) {}
    friend Rational operator+(int, const Rational&);
    friend Rational operator+(const Rational&, int);
    ...略
};

...略

Rational operator+(int lhs, const Rational &rhs) { 
    return Rational(
        lhs * rhs.denom + rhs.numer,
        rhs.denom
    ); 
} 

Rational operator+(const Rational &lhs, int rhs) { 
    return Rational(
        lhs.numer + rhs * lhs.denom,
        lhs.denom
    ); 
} 

...略

int main() {
    Rational a(1, 2);
    Rational b(2, 3);

    ...略

    cout << (1 + a).to_string() << endl;
    cout << (a + 1).to_string() << endl;

    return 0;
}

有時候,你不能或不想修改物件的類別原始碼,例如,想重載 cout<< 運算子,這時就只能選擇實作為非成員函式:

#include <iostream>
#include <string>
using namespace std;

class Rational {
    ...略
public:
    ...略
    string to_string() const;
};

...略

string Rational::to_string() const { 
    return std::to_string(this->numer) + "/" + std::to_string(this->denom);

} 

ostream& operator<<(ostream &os, const Rational &r) {
    return os << r.to_string();
}

int main() {
    Rational a(1, 2);
    Rational b(2, 3);

    cout << (a + b) << endl;
    cout << (a - b) << endl;
    cout << (a * b) << endl;
    cout << (a / b) << endl;
    cout << (++a) << endl;
    cout << (--a) << endl;
    cout << (b++) << endl;
    cout << (b--) << endl;

    cout << (1 + a) << endl;
    cout << (a + 1) << endl;

    return 0;
}

大部份的運算子都是可以被重載的,然而 .::.*?: 不能重載。