目的
C++有許多內置的數據類型,包括int,char,double等,每一種類型都有許多
運算符,例如加,減,乘,除等。當用戶定義了類的對象時,兩個對象之間是不能進行這些操作的,比如hyong類的對象a+b,這樣的語句如果沒有重載+運算符就會出錯。但C++允許用戶把這些運算符添加到自已的類中以方便類對象之間的運算就像
內置類型的運算一樣方便,比如對象a+b這樣就很明白更容易懂,當然也可以在類中定義一個對象間相加的函式,比如a.add(b)調用函式add()以實現兩個對象a和b相加,但是這條語句沒有比a+b更容易讓人理解。
實現
概述
要實現操作符重載就要使用操作符
重載函式,操作符重載函式用關鍵字operator實現,其形式為:返回類型 operator 操作符 (參數列表){}。操作符重載函式是一個函式,只不過這個函式的函式名為operator再加上後面要重載的操作符而已,比如要重載+號,則:hyong operator +(hyong m){}這就聲明了一個返回類型為hyong的操作符函式,其函式名為operator +
不同情況
當操作符
重載函式作為
類的成員函式時,操作符重載函式的參數會比作為友元或者獨立於類的操作符重載函式少一個參數,因為操作符重載類成員函式把調用該函式的對象作為函式的第一個參數,也就是隱含的this
指針指向調用該函式的第一個對象,所以會少一個參數。
調用方式
調用類中的操作符重載函式的方法:當調用類中定義的操作符重載函式時最左邊的對象是調用操作符重載函式的對象。比如在類hyong中重定義的+操作符 hyong operator +(hyong m){},有類hyong的對象m和n則調用操作符重載函式的方法有m+n和m.operator +(n),前一條語句會自動轉換為後面這條語句,且m+n的
表達式中最左邊的對象是調用操作符重載函式的對象,而最右邊的那個將被作為參數傳送。也就是說n+m會轉換成n.operator +(m)。要記住當調用類中定義的操作符重載函式時最左邊的對象是調用操作符重載函式的對象。
調用友元或獨立的操作符重載函式的方法:當調用類的友元操作符重載函式或獨立的操作符函式時語句m+n會轉換為顯示的調用方式,比如有友元或獨立操作符重載函式hyong operator +(hyong a,hyong b){}則當出現m+n時會轉換成語句operator +(m,n)
表達式的第一個對象傳給第一個參數,第二個對象傳給第二個參數。
特殊情況
一般來說操作符
重載函式一般不要求作為類的成員函式或者是友元函式,一般情況下可以將操作符重載函式作為類的成員函式。但是有一種情況必須要求操作符函式作為類的
友元函式或者是獨立的函式,就是一個
內置類型和對象相加的情況。比如有語句m+1和1+m第一條可以在類中定義操作符函式的形式為hyong operator +(int i){},語句m+1可以調用這個函式是正確的,但對於1+m就不能調用這個函式了,因為類中的操作符重載函式是最左邊的對象是調用該函式的對象,但1+m最左邊的是一個內置
整型類型1,所以不會調用這條語句,對於這種語句就只能把操作符重載函式定義為獨立的函式或類的
友元函式即形如hyong operator +(int i,hyong a){}這樣1+m就會轉換成operator +(1,m)這樣就是正確的。當然如果這個操作符
重載函式需要訪問類中的私有成員時,就應把該函式定義為類的友元函式,如果不需要訪問類中的私有成員,則可以定義為友元也可以定義為獨立函式。
必須把它作為類成員函式的運算符有:(),[],->;和任何賦值運算符,重載這些運算符時必須把操作符函式聲明為
類的成員函式。
重載限制
並不是所有的操作符都能被重載。除了. ,.* ,:: ,? : ,sizeof,typeid這幾個運算符不能被重載,其他運算符都能被重載
重載不能改變該
運算符用於內置類型時的函義,程式設計師不能改變運算符+用於兩個int型時的含義。
運算符函式的參數至少有一個必須是類的對象或者類的對象的引用。這種規定可以防止程式設計師運用運算符改變內置類型的函義。
重載不能改變運算符的優先權。
重載不能改變運算符的結合律。
重載不能改變運算符
運算元的個數。比如+需要兩個運算元,則重載的+也必須要有兩個運算元。
返回類型
在某些情況下操作符函式必須返回一個對象類型作為返回值,比如有hyong的對象a,b,c則語句a=b+c其中重載的+操作符就必須返回一個hyong類型的一個對象,以便賦給對象a,不然該語句就會出錯。當然你可以在語句中返回一個臨時對象,也可以返回一個對象的引用,或者返回this
指針,不過返回臨時對象會浪費記憶體開銷,所以最好返回類對象的一個引用。
參數傳遞
操作符函式可以按值傳遞也可以按引用傳遞,這根據操作符而定,比如對於+
運算符既可以把對象按值傳遞給操作符函式也可以按引用傳遞給操作符函式,而且+操作符不會改變原
運算元的值,所以應把傳遞類型聲明為const,比如hyong operator +(const hyong &a,const hyong &b){}。但對於要改變其自身值的操作符比如++運算符,就必須傳遞引用,且不能把該引用聲明為const類型,因為如果運算元按值傳遞的話,傳遞給運算元函式的將是一個對象的副本,兩個副本是獨立的,不能改變到原對象的值,所以應按引用傳遞對象,比如hyong operator ++(hyong &a){}。
運算符重載示例
運算符++
注意++有前綴和後綴之別,前綴形式是變數先加1然後執行
表達式,而後綴形式則是先執行表達式然後再使變數加1,所以在執行後綴的++運算符時應先返回對象的原始值,然後才對對象加1。
//前綴++返回的是左值,即該對象+1之後的引用。後綴++返回是的右值。類似於{ int tmp = a; a=a+1; return tmp;} 並不是先執行表達式再加1 而是返回+1之前的值。可以通過下面的程式印證: int a =5; (++a) = 10;可以通過編譯 (a++) = 10;無法通過編譯
默認的帶有一個參數的++運算符函式是前綴++運算符,要重載後綴的++運算符必須採用另一種方式實現。
重載後綴的++
運算符時應給函式多增加一個int參數,該int參數不會使用,應忽略他,該參數只是為了讓
編譯器區分++運算符的前綴和後綴形式之間的區別。比如重載後綴++運算符的
友元函式形式為hyong operator ++(hyong &a,int i){}後面的參數int i沒有實際意義,應忽略他。
例:重載++運算符的例子
class A
{public: int b; A(){b=0;} A(int i){b=i;} ~A(){cout<<"xi"<<"\n";}
const A & operator ++(){ ++b; return *this;} }; //定義前綴形式的++運算符,函式的返回類型既可以返回引用也可以是返回值,但返回引用不會增加記憶體開銷。返回類型可以是任意的,比如可以是int型,也可以是void,即沒有返回值,但這裡的返回類型必須是類類型A,因為在main函式中
表達式用於
賦值運算符的左邊,且把結果賦給了一個類A的對象。返回const的原因是防止++++k這樣的情況出現,有const就不能再改變返回對象的值即不能再對++k作增量運算。
const A & operator ++(A &j,int i) //定義獨立的後綴形式的++運算符,這裡必須把第一個參數聲明為對對象的引用,因為++運算符會改變原始對象的值,如果不是引用就不能改變原始對象的值,也就達不到++運算符的效果。注意int i參數是不使用的,只是讓
編譯器區分是前綴還是後綴的++運算符。
{A t(j); //定義一個A類對象t,因為後綴形式的++運算符是先執行
表達式後進行增量運算,所以這裡應生成一個臨時對象以便返回對象的原始值
++j.b; //注意,因為獨立函式既不是類的
友元又不是類的成員,所以這裡沒有this
指針,也不能直接訪問類的成員。
return t;} //返回對象t,這裡會生成一個臨時對象。
int main()
{ A m,n,k;
m=++k; //調用前綴形式的++類成員運算符函式,語句等價於m=k.operator ++();因為前綴的++是類的成員,所以只能用點運算符調用,形如operator ++(k)將是錯誤的。
cout<<m.b<<k.b; //輸出11,前綴++是先使變數加再執行
表達式,即對象k的值先加,然後再賦給對象m
n=k.operator ++ (); //顯示調用前綴的++類成員運算符函式。同m=++k。
cout<<n.b<<k.b; //輸出22。
n=k++; //調用後綴形式的獨立++運算符函式,該語句等價於n=operator ++(k,1),其中後面的是沒有意義的參數,只是為了讓
編譯器區別是前綴還是後綴
cout<<n.b<<k.b; //輸出23,注意,這裡n的值沒有增加,因為後綴++是先執行
表達式後使變數加。
n= operator ++(k,1); //顯示調用後綴的++獨立運算符函式,同n=k++。注意整形
實參在這裡沒有實用價值,但必須得有,以便指明是調用的後綴++形式。
cout<<n.b<<k.b; } //輸出34。
運算符+
要注意重載二元運算符時如果有形如1+m這種類型的
表達式就必須把該操作符函式定義為非類的成員的形式。因為類中的操作符重載函式是最左邊的對象是調用該函式的對象
class A
{public: int b; A(){b=0;} ~A(){cout<<"xi"<<"\n";}
explicit A(int i){b=i;} //這裡需要explicit關鍵字以防止自動的隱式類型轉換,不然語句const A &operator +(const A &j)和friend const A &operator +(const A &j,const int i)將出錯二義性問題。也就是第一個操作符函式可以用隱式類型轉換用語句m+3來調用。
//const A operator +(const A &j){b=b+j.b;return *this;} //對於+操作符不會改變被加的
運算元的值,但這裡改變了調用該函式的對象的值,最好不要這樣做。
const A operator +(const A &j){A t; t.b=b+j.b; return t;} //定義一個臨時對象t以防止修改調用該函式的兩個運算元的值。
friend const A &operator +(const A &j,const int i); };//+操作符函式不應該改變調用該函式的兩個運算元的值,所以這裡將參數聲明為const
const A operator +(const A &j,const int i){A t;t.b=j.b+i; return t;} //定義友元函式以實現"對象+內置類型"這種類型的運算
const A operator +(const int i,const A &j) {A t; t.b=j.b+i;return t;} //因為類A沒有私有成員,該函式也不訪問類中的私有成員所以可以不用定義為類的
友元,這個函式將實現,"內置類型+對象"的這種運算
//對於這種操作符
重載函式不能定義為
類的成員函式,因為類的成員函式的第一個參數是指向類對象的一個this
指針,在這裡第一個參數是內置類型不是類的對象,所以必須把這種操作符函式定義為非成員的形式。
int main()
{ A m⑴,n⑵,k;
k=m+3; //調用operator +(const A &j,const int i)這個類的
友元操作符函式,以實現對象和內置類型相加,該語句和operator +(m,3)一樣。
cout<<m.b<<k.b; //輸出14,這裡沒有改變對象m的值,這是應該的。
k=operator +(m,3); cout<<m.b<<k.b;// 輸出14。和上面m+3的語句一樣,這是顯示調用操作符函式的形式,記住操作符
重載函式是一個函式,只是他的名字比較特別,名字為operator加上後面的操作符而已
k=m+n; //調用類成員操作符函式operator +(const A &j),實現兩個對象相加,這條語句會自動轉換為m.operator +(n)的形式調用操作符函式。記住最左邊的對象是調用類成員操作符函式的對象。cout<<m.b<<n.b<<k.b; //輸出123,這裡沒有改變對象m和n的值,實現了對象m和n相加 k=n.operator +(m); cout<<m.b<<n.b<<k.b; //顯示調用成員操作符函式operator +(const A &j)的形式,這種形式和語句n+m等價。
k=4+m; //調用操作符函式operator +(const int i,const A &j)以實現內置類型和一個對象相加,語句和operator +(3,m)等價。cout<<m.b<<k.b; //輸出15。k=operator +(4,m); cout<<m.b<<k.b; } //輸出15,語句和+m相同。
運算符=
1. 注意重載賦值運算符和[],(),->;運算符必須定義為
類的成員函式。
2. 注意:如果程式不提供顯示的
賦值運算符則系統會提供一個默認的賦值運算符。
3. 什麼時候重載賦值運算符:當類中含有
指針成員時,一般都要重定義類的賦值運算符。
4. 重載賦值運算符時應有處理語句m=m的情況。其中m是某一個類的對象。如果不處理這樣的語句有時會出現問題,具體什麼問題有待調查。可以用this指針來做處理,比如有語句const A & operator(A &j)則可以用if(this==&j) return *this;這樣的語句來處理,即比較當前調用
賦值運算符函式的對象的地址和被賦值的對象的地址,如果地直相等說明是同一個對象。
5. 重載賦值運算符時應返回一個對象。因為賦值運算符的左邊是一個對象,所以重載賦值運算符應返回一個類的對象,為了避免不必要的開銷,最好是返回一個類的對象的引用。
class A
{
public:
int b;
A(){b=1;}
A(int i){ b=i;}
const A & operator =(const A & j) //返回一個類的對象的引用
{
/* 用this和j的地址來檢查是否是對自身的賦值的情況,如果調用
賦值運算符函式的地址和
被賦值的對象的地址相等,則說明是同一個對象,就返回當前對象。
*/
if(this==&j) return *this;
b=j.b;
return *this;
}
};
int main()
{
A m⑵; A n; n=m; cout<<n.b;
n=n; //對象對自已賦值的情況。
cout<<n.b;
}