C++運算子過載的概念和原理

2020-07-16 10:04:23
如果不做特殊處理,C++ 的 +、-、*、/ 等運算子只能用於對基本型別的常數或變數進行運算,不能用於物件之間的運算。

有時希望物件之間也能用這些運算子進行運算,以達到使程式更簡潔、易懂的目的。例如,複數是可以進行四則運算的,兩個複數物件相加如果能直接用+運算子完成,不是很直觀和簡潔嗎?

利用 C++ 提供的“運算子過載”機制,賦予運算子新的功能,就能解決用+將兩個複數物件相加這樣的問題。

運算子過載,就是對已有的運算子賦予多重含義,使同一運算子作用於不同型別的資料時產生不同的行為。運算子過載的目的是使得 C++ 中的運算子也能夠用來操作物件。

運算子過載的實質是編寫以運算子作為名稱的函數。不妨把這樣的函數稱為運算子函數。運算子函數的格式如下:

返回值型別  operator  運算子(形參表)
{
    ....
}

包含被過載的運算子的表示式會被編譯成對運算子函數的呼叫,運算子的運算元成為函數呼叫時的實參,運算的結果就是函數的返回值。運算子可以被多次過載。

運算子可以被過載為全域性函數,也可以被過載為成員函數。一般來說,傾向於將運算子過載為成員函數,這樣能夠較好地體現運算子和類的關係。來看下面的例子:
#include <iostream>
using namespace std;
class Complex
{
public:
    double real, imag;
    Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { }
    Complex operator - (const Complex & c);
};
Complex operator + (const Complex & a, const Complex & b)
{
    return Complex(a.real + b.real, a.imag + b.imag); //返回一個臨時物件
}
Complex Complex::operator - (const Complex & c)
{
    return Complex(real - c.real, imag - c.imag);  //返回一個臨時物件
}
int main()
{
    Complex a(4, 4), b(1, 1), c;
    c = a + b;  //等價於 c = operator + (a,b);
    cout << c.real << "," << c.imag << endl;
    cout << (a - b).real << "," << (a - b).imag << endl;  //a-b等價於a.operator - (b)
    return 0;
}
程式的輸出結果是:
5,5
3,3

程式將+過載為一個全域性函數(只是為了演示這種做法,否則過載為成員函數更好),將-過載為一個成員函數。

運算子過載為全域性函數時,引數的個數等於運算子的目數(即運算元的個數);運算子過載為成員函數時,引數的個數等於運算子的目數減一。

如果+沒有被過載,第 21 行會編譯出錯,因為編譯器不知道如何對兩個 Complex 物件進行+運算。有了對+的過載,編譯器就將a+b理解為對運算子函數的呼叫,即operator+(a,b),因此第 21 行就等價於:

c = operator+(a, b);

即以兩個運算元 a、b 作為引數呼叫名為operator+的函數,並將返回值賦值給 c。

第 12 行,在 C++ 中,“類名(建構函式實參表)”這種寫法表示生成一個臨時物件。該臨時物件沒有名字,生存期就到包含它的語句執行完為止。因此,第 12 行實際上生成了一個臨時的 Complex 物件作為 return 語句的返回值,該臨時物件被初始化為 a、b 之和。第 16 行與第 12 行類似。

由於-被過載為 Complex 類的成員函數,因此,第 23 行中的a-b就被編譯器處理成:

a.operator-(b);

由此就能看出,為什麼運算子過載為成員函數時,引數個數要比運算子目數少 1 了。