1. 程式人生 > >C++ 面向物件- -運算子的過載(一)

C++ 面向物件- -運算子的過載(一)

 

運算子過載的方法

運算子過載的方法就是定義一個過載運算子的函式,使指定的運算子不僅能實現原有的功能,還能實現在函式中指定的新加的功能。在使用被過載的運算子時,系統就會自動呼叫該函式,以實現相應的功能,也就是說,運算子的過載是通過定義函式實現的運算子的過載實質上就是函式的過載

過載運算子的函式一般形式: 函式型別 operator 運算子名稱(形參表){ 對運算子的過載處理 } ; 例如過載 “+” 來實現兩個複數相加的運算,那麼函式可以是: Complex operator + ( Complex a , Complex b) ; 在其格式中, operator 是關鍵字,是專門用於定義過載運算子的函式, 運算子名稱就是 C++中已有的運算子 。函式名是由 operator 和運算子 組成的,上面的 “operator +” 就是函式名,意思是對運算子+ 過載的函式 。可以說是函式 “operator +” 過載了運算子 “+” ,在程式中執行兩個複數相加時就自動呼叫該過載函式。

下面是一個對 “+” 過載以實現兩個複數相加的例子:

#include <iostream>
using namespace std;
class Complex{
		double real;
		double imag;
	public:
		Complex(double r=0,double i=0):real(r),imag(i){}
		void display(){
			cout<<"("<<real<<","<<imag << "i)" <<endl;
		}
        //Complex complex_add(Complex a);
		Complex operator+(Complex a);
};
/*Complex Complex::complex_add(Complex a){
    Complex b;
	b.real=real+a.real ;
	b.imag=imag+a.imag ;
	return b;
}*/
Complex Complex::operator+(Complex a){
	Complex b;
	b.real=real+a.real ;
	b.imag=imag+a.imag ;
	return b;
}
int main(){
    Complex c1(1,1);
	cout<<"c1=";c1.display();
    Complex c2(2,2);
	cout<<"c2=";c2.display();
	Complex c3;
    c3=c1+c2;
	cout<<"c1+c2=";c3.display();
}

上邊的程式中有對運算子 “+” 的過載實現兩個複數相加,也有通過呼叫函式來實現複數相加,其結果都是一樣的。在上邊的例子中,運算子 “+” 被過載過,系統在編譯時將程式中的表示式 c1+c2 解釋為 c1.operator+ (c2) ,前邊也已提過,“operator+”是一個函式名,所以這也算是通過函式呼叫來實現的,只是這個運算子過載函式是系統自動呼叫執行。

上邊的運算子過載函式還可以簡寫:

Complex Complex::complex_add(Complex a){
	return Complex(real+a.real,imag+a.imag) ;
}

Complex 用來建立一個臨時物件,它沒有物件名,是一個無名物件,return 語句將此臨時物件作為函式返回值。

另外可以試試實現一個整數和一個複數的相加,然後把它們互換一下位置再試試;函式的位置改變試試,作為成員函式、友元函式、普通函式。

 

過載運算子的規則

在運算子過載時一定要遵守這幾個規則:

  • 系統不允許使用者自己定義新的運算子,只能對C++已有的運算子進行過載。
  • C++中允許過載的運算子:
雙目算術運算子 +(加)、-(減)、*(乘)、/(除)、%(取模)
關係運算符 ==(等於)、!=(不等於)、<(小於)、>(大於)、<=(小於等於)、>=(大於等於)
邏輯運算子 || (邏輯或)、&&(邏輯與)、!(邏輯非)
單目運算子 +(正)、-(負)、*(指標)、&(取地址)
自增自減運算子 ++(自增)、--(自減)
位運算子 |(按位或)、&(按位與)、~(按位取反)、^(按位異或)、<<(左移)、>>(右移)
賦值運算子 =、+=、-=、/=、%=、|=、^=、<<=、>>=
空間申請與釋放 new、delete、new[]、delete[]
其他運算子 ()(函式呼叫)、->(成員訪問)、->*(成員指標訪問)、,(逗號)、[](下標)

不允許過載的運算子只有 5 個:. (成員訪問運算子)、*(成員指標訪問運算子)、::(域運算子)、sizeof(長度運算子)、?:(條件運算子)。前兩個是為了保證訪問成員的功能不能被改變,域運算子和sizeof運算子的運算物件是型別而不是變數或一般表示式,不具備過載的特徵。

這裡基本上把運算子都包含了進來,但有很多基本上都不怎麼用,瞭解一下就好。

  • 過載不能改變運算子運算物件(即運算元)的個數。
  • 過載不能改變運算子的優先級別,如果希望改變某運算子的優先順序,只能用加圓括號的方法強制改變過載運算子的運算順序。
  • 過載不能改變運算子的結合性,如賦值運算子“=” 是右結合性(從右向左)。
  • 過載運算子的函式不能有預設的引數,否則就改變了運算子引數的個數。
  • 過載的運算子必須和使用者定義的自定義型別的物件一起使用,其引數至少應有一個類物件(或類物件的引用),也就是說引數不能全部是C++中的標準型別,防止使用者修改用於標準型別資料的運算子的性質。
  • 用於類物件的運算子一般必須過載,但有兩個例外,運算子“=”和 “&” 不用過載。
  • 從理論上說,可以將一個運算子過載為執行任意的操作,但應當使過載運算子的功能類似於標準型別資料時所實現的功能。如
int operator+(int a,int b){
	return (a-b) ;
}

 

運算子過載函式作為成員函式和友元函式

對運算子過載的函式有兩種處理方式:1)把運算子過載的函式作為類的成員函式;2)運算子過載的函式不是類的成員函式(可以是一個普通函式),在類中把它宣告為友元函式。

如前邊寫的兩個複數相加的例子,就是把運算子過載作為類的成員函式,其有一個引數是隱含的,運算子函式用 this 指標隱式地訪問類物件的成員,因此過載函式中只需要一個形參即可。

對友元運算子過載函式舉一個例子:

#include <iostream>
using namespace std;
class Complex{
		double real;
		double imag;
	public:
		Complex(double r=0,double i=0):real(r),imag(i){}
		void display(){
			cout<<"("<<real<<","<<imag << "i)" <<endl;
		}
		friend Complex operator+(Complex a,Complex b);		//過載函式作為友元函式 
};

Complex operator+(Complex a,Complex b){		//注意和之前不一樣的還有 友元函式不需要域運算子 “::” 。 
	Complex c;
	c.real=b.real+a.real ;
	c.imag=b.imag+a.imag ;
	return c;
}
int main(){
    Complex c1(1,1);
    cout<<"c1=";
    c1.display();
    Complex c2(2,2);
    cout<<"c2=";
    c2.display();
    Complex c3;
    c3=c1+c2;
    c3.display();
}

過載運算子函式作為友元函式和成員函式區別不大,怎麼用都行,但需要知道的是,如果將運算子過載函式作為成員函式,它可以通過 this 指標自由地訪問奔類中的資料成員,因此可以少寫一個函式的引數,但必須要求運算表示式(如 c1+c2)中的第一個引數(即運算子左邊的運算元)是一個類物件,而且與運算子函式的型別相同,因為必須通過類的物件去呼叫該類的成員函式,而且只有運算子過載函式返回值與該物件同類型,運算結果才有意義。像前面提到過的問題,如果使一個整數和一個複數相加該怎麼寫,如果將運算子過載函式作為成員函式只能(c1+ int ),它們兩個的順序是不可改變的;將雙目運算子過載為友元函式時,由於函式不是該類的成員函式,因此在函式的形引數表列中必須有兩個引數,不能省略,順序任意,實參和形參的型別對應就行(如果想在主函式中同時實行 c2=c1+i 和 c2=i+c1 這兩個部分,那麼需要對這個友元過載函式過載兩次,第二次將形參的順序互換一下,那麼在編譯的時候,系統會根據表示式額形式選擇呼叫與之匹配的運算子過載函式)。

注意:由於友元的使用會破壞類的封裝,因此從原則上說,儘量將運算子函式作為成員函式。但後面的流插入和流提取運算子、型別轉換運算子都不能定義為類的成員函式,只能是作為友元函式。一般將單目運算子和複合運算子(+=、-=、/=、*=、&=、!=、%=、`=、<<=、>>=)過載為成員函式,一般將雙目運算子過載為友元函式。