一.運算子多載的含義與定義方式
l C++已有的運算子只適合處理C++的基礎資料型別 (Elementary Data Type)。
l C++允許重新定義已有的運算子(運算子多載),以便它能處理常式員定義類型(類類型)。
l 運算子多載就是賦予已有的運算子多重含義。運算子多載與函數重載類似,是它的特殊類型。
l C++通過重新定義運算子,使它能夠用於特定類的對象執行特定的功能。
l 通過對+,-,*,/運算子的重新定義,使它們可以完成複數、分數等不同類的對象的加、減、乘、除運算操作。增強了C++語言的擴充能力。
l 先建立一個運算子函數,一般定義成類的成員函數或友元函數。
二.重載一個運算子原則:
1.不能改變運算子的初始意義。
2.不能改變運算子的參數數目。如重載運算子+時只用一個運算元是錯誤的。
3.運算子函數不能包括預設的參數。
4.絕大部分C++運算子都可以重載,除以下的例外:
. :: .* ?
5.除賦值運算子外,其它運算子函數都可以由衍生類別繼承。
6.運算子多載不改變運算子的優先順序和結合性,也不改變運算子的文法結構,即單目、雙目運算子只能重載為單目、雙目運算子。
7.運算子的重載實際上是函數的重載。編譯器對運算子多載的選擇,遵循函數重載的選擇原則。當遇到不很明顯的運算子時,編譯器將去尋找參數匹配的運算子函數。
8.運算子多載可使程式更簡潔,使運算式更直觀,增強可讀性。但使用不宜過多。
9.重載運算子含義必須清楚:
如有一個類Time,它有三個資料成員時、分、秒
calss Time{
public:
Time() {hours=minutes=seconds=0;}
Time(int h,int m,int s){ hours=h; minutes=m; seconds=s;}
private:
int hours, minutes, seconds;
};
Time t1(8,10,20), t2(9,15,30), t3;
t3=t1+t2;
這裡加法(+)運算用於類Time的對象,就是含義不清的。所以不能給類Time定義重載運算子+。
三.運算子多載函數的兩種形式:
成員函數形式和友元函數形式,他們都可訪問類中的私人成員。
1.重載為類的成員函數
1)X類中重載一元運算子@
傳回型別 X::operator@( )
{ … }
不指定參數,因為它已帶有一個隱含的this參數,對X類的一個對象obj:
運算式 C++編譯器的解釋
@obj operator @(obj)
obj@ operator @(obj,0)
2) X類中重載二元運算子@
傳回型別 X::operator@(參數說明)
{ … }
由於類的成員函數帶有一個this參數,此時只能指定一個參數,對obj對象:
運算式 C++編譯器的解釋
obj1@obj2 obj1·operator@(obj2)
在多數情況下,將運算子多載為類的成員函數和類的友元函數都是可以的。但成員函數運算子與友元函數運算子也具有各自的一些特點:
(1) 一般情況下,單目運算子最好重載為類的成員函數;雙目運算子則最好重載為類的友元函數。
(2) 以下一些雙目運算子不能重載為類的友元函數:=、()、[]、->。
(3) 類型轉換函式只能定義為一個類的成員函數而不能定義為類的友元函數。
(4) 若一個運算子的操作需要修改對象的狀態,選擇重載為成員函數較好。
(5) 若運算子所需的運算元(尤其是第一個運算元)希望有隱式類型轉換,則只能選用友元函數。
(6) 當運算子函數是一個成員函數時,最左邊的運算元(或者只有最左邊的運算元)必須是運算子類的一 個類對象(或者是對該類對象的引用)。如果左邊的運算元必須是一個不同類的對象,或者是一個內部 類型的對象,該運算子函數必須作為一個友元函數來實現。
(7) 當需要重載運算子具有可交換性時,選擇重載為友元函數。
例:給複數運算子多載的四則運算子。複數由實部和虛部構造,定義一個複數類,再在類中重載複數四則運算的運算子。
#include<iostream.h>
class complex
{ private:
float real,imag;
public:
complex(float r=0,float i=0);
complex operator+(complex &c);
complex operator-(complex &c);
friend void print(complex &c);
};
complex::complex(float r,float i)
{ real=r; imag=i; }
complex complex::operator+(complex &c)
{ float r=real+c.real; float i=imag+c.imag;
return complex(r,i) ; }
complex complex::operator-(complex &c)
{ float r=real-c.real; float i=imag-c.imag;
return complex(r, i) ; }
void print(complex &c )
{ cout<<'('<<c.real<<','<<c.imag<<')'<<endl; }
void main( )
{ complex c1(2.5,3.7), c2(4.2,6.5) ;
complex c;
c=c1-c2; //c=c1·operator-(c2)
print(c);
c=c1+c2; //c=c1·operator+(c2)
print(c);
}
該程式中定義一個complex類,定義了2個成員函數作為運算子多載函數。
c1+c2編譯器解釋為:c1.operator+(c2)
c1、c2是complex 類的對象。operator+()是運算子+的重載函數。
該運算子多載函數僅有一個參數c2。當重載為成員函數時,雙目運算子僅有一個參數。
對單目運算子,重載為成員函數時,不能再顯式說明參數。
重載為成員函數時,總是隱含了一個參數,即this指標,它是指向調用該成員函數對象的指標。
在重載運算子函數中,operator +( )中參數用引用傳遞而不用指標傳遞。
因為指標傳遞存在程式的可讀性問題。如操作符重載聲明為:
complex operator +( complex *c);
則調用時
complex c1(2.0,3.0),c2(4.0,-2.0),c3;
c3=&c1+&c2; 會認為是c1的地址和c2的地址相加
而聲明為complex operator +(const complex &c);
則 c3=c1+c2;
2. 重載為友元函數
運算子多載函數還可以為友元函數。當重載為友元函數時,沒有隱含的參數this指標。這樣對雙目運算子,友元函數有2個參數,對單目運算子,友元函數有1個參數。但有些運算子不能重載為友元函數,它們是:=、()、[]、->。
1) X類中重載一元運算子@
傳回型別 operator@(X&obj )
{ … }
只能為友元運算子指定一個參數,對X類的一個對象obj:
運算式 C++編譯器的解釋
@obj operator @(obj)
obj@ operator @(obj,0)
2) X類中重載二元運算子@
傳回型別 operator@(參數說明1,參數說明2)
{……}]
兩個參數中必須至少有一個是X類類型,設有對象obj1,obj2
運算式 C++編譯器的解釋
obj1@obj2 operator @(obj1,obj2)
例:用友元函數代替成員函數編上述程式:
#include<iostream.h>
class complex
{ private:
float real,imag;
public:
complex(float r=0,float i=0);
friend complex operator+(complex &c1,complex &c2);
friend complex operator-(complex &c1,complex &c2);
friend void print(complex &c);
};
complex::complex(float r,float i)
{ real=r; imag=i; }
complex operator+(complex &c1,complex &c2)
{ float r=c1.real+c2.real; float i=c1.imag+c2.imag;
return complex(r,i) ; }
complex operator-(complex &c1,complex &c2)
{ float r=c1.real-c2.real; float i=c1.imag-c2.imag;
return complex(r, i) ; }
void print(complex &c )
{ cout<<'('<<c.real<<','<<c.imag<<')'<<endl; }
void main( )
{ complex c1(2.5,3.7), c2(4.2,6.5) ;
complex c;
c=c1-c2; //c=c1·operator-(c2)
print(c);
c=c1+c2; //c=c1·operator+(c2)
print(c);
}
雙目運算子多載為成員函數時,僅有一個參數,另一個被隱含;
重載為友元函數時,有兩個參數,沒有隱含參數;
c1+c2編譯器解釋為:operator+( c1,c2)
調用如下函數,進行求值,
complex operator +(const complex &c1,consrt complex &c2)
結論1:對二元運算子,將它重載為一個友元函數比重載為一個成員函數要便於使用。作為一個友元函數,二元運算子不要求第一個參數一定為某類的對象。
結論2:對一元運算子,將它重載為一個成員函數最恰當。重載為友員函數也可以。
例:
#include <iostream.h>
class A{
public:
A(){X=Y=0;}
A(int i,int j){X=i;Y=j;}
A(A &p){X=p.X;Y=p.Y;}
A& operator =(A &p);
int getX(){return X;}
int getY(){return Y;}
private:
int X,Y;
};
A& A::operator =(A &p)
{
X=p.X;
Y=p.Y;
cout<<"Assignment operator called./n";
return *this;
}
void main()
{
A a(7,8);
A b;
b=a;
cout<<b.getX()<<","<<b.getY()<<endl;
}
Assignment operator called.
7,8
該程式中,在類A中定義了一個賦值運算子函數,被定義為成員函數。
b=a解釋為: b.operator=(a)
調用下列函數:A& A::operator=(A &p)完成賦值操作。