複製鏈接
請複製以下鏈接發送給好友

操作符重載

鎖定
操作符重載,計算機學科概念,就是把已經定義的、有一定功能的操作符進行重新定義,來完成更為細緻具體的運算等功能。操作符重載可以將概括性的抽象操作符具體化,便於外部調用而無需知曉內部具體運算過程。
中文名
操作符重載
外文名
operators overloading
定    義
操作符重載就是把操作符重新定義
目    的
便於外部調用
參數傳遞方式
按值傳遞也可以按引用傳遞
應用學科
計算機科學
測繪科學

操作符重載目的

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指針指向調用該函數的第一個對象,所以會少一個參數。

操作符重載調用方式

  1. 調用類中的操作符重載函數的方法:當調用類中定義的操作符重載函數時最左邊的對象是調用操作符重載函數的對象。比如在類hyong中重定義的+操作符 hyong operator +(hyong m){},有類hyong的對象m和n則調用操作符重載函數的方法有m+n和m.operator +(n),前一條語句會自動轉換為後面這條語句,且m+n的表達式中最左邊的對象是調用操作符重載函數的對象,而最右邊的那個將被作為參數傳送。也就是説n+m會轉換成n.operator +(m)。要記住當調用類中定義的操作符重載函數時最左邊的對象是調用操作符重載函數的對象。
  2. 調用友元或獨立的操作符重載函數的方法:當調用類的友元操作符重載函數或獨立的操作符函數時語句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)這樣就是正確的。當然如果這個操作符重載函數需要訪問類中的私有成員時,就應把該函數定義為類的友元函數,如果不需要訪問類中的私有成員,則可以定義為友元也可以定義為獨立函數。
必須把它作為類成員函數的運算符有:(),[],->;和任何賦值運算符,重載這些運算符時必須把操作符函數聲明為類的成員函數

操作符重載重載限制

  1. 並不是所有的操作符都能被重載。除了. ,.* ,:: ,? : ,sizeof,typeid這幾個運算符不能被重載,其他運算符都能被重載
  2. 重載不能改變該運算符用於內置類型時的函義,程序員不能改變運算符+用於兩個int型時的含義。
  3. 運算符函數的參數至少有一個必須是類的對象或者類的對象的引用。這種規定可以防止程序員運用運算符改變內置類型的函義。
  4. 重載不能改變運算符的優先級。
  5. 重載不能改變運算符的結合律。
  6. 重載不能改變運算符操作數的個數。比如+需要兩個操作數,則重載的+也必須要有兩個操作數。

操作符重載返回類型

在某些情況下操作符函數必須返回一個對象類型作為返回值,比如有hyong的對象a,b,c則語句a=b+c其中重載的+操作符就必須返回一個hyong類型的一個對象,以便賦給對象a,不然該語句就會出錯。當然你可以在語句中返回一個臨時對象,也可以返回一個對象的引用,或者返回this指針,不過返回臨時對象會浪費內存開銷,所以最好返回類對象的一個引用。 [1] 

操作符重載參數傳遞

操作符函數可以按值傳遞也可以按引用傳遞,這根據操作符而定,比如對於+運算符既可以把對象按值傳遞給操作符函數也可以按引用傳遞給操作符函數,而且+操作符不會改變原操作數的值,所以應把傳遞類型聲明為const,比如hyong operator +(const hyong &a,const hyong &b){}。但對於要改變其自身值的操作符比如++運算符,就必須傳遞引用,且不能把該引用聲明為const類型,因為如果操作數按值傳遞的話,傳遞給操作數函數的將是一個對象的副本,兩個副本是獨立的,不能改變到原對象的值,所以應按引用傳遞對象,比如hyong operator ++(hyong &a){}。 [1] 

操作符重載運算符重載示例

操作符重載運算符++

注意++有前綴和後綴之別,前綴形式是變量先加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] 

操作符重載運算符+

要注意重載二元運算符時如果有形如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相同。 [2] 

操作符重載運算符=

1. 注意重載賦值運算符和[],(),->;運算符必須定義為類的成員函數
2. 注意:如果程序不提供顯示的賦值運算符則系統會提供一個默認的賦值運算符。
3. 什麼時候重載賦值運算符:當類中含有指針成員時,一般都要重定義類的賦值運算符。
4. 重載賦值運算符時應有處理語句m=m的情況。其中m是某一個類的對象。如果不處理這樣的語句有時會出現問題,具體什麼問題有待調查。可以用this指針來做處理,比如有語句const A & operator(A &j)則可以用if(this==&j) return *this;這樣的語句來處理,即比較當前調用賦值運算符函數的對象的地址和被賦值的對象的地址,如果地直相等説明是同一個對象。
5. 重載賦值運算符時應返回一個對象。因為賦值運算符的左邊是一個對象,所以重載賦值運算符應返回一個類的對象,為了避免不必要的開銷,最好是返回一個類的對象的引用。
6. 重載賦值運算符時必須是類的成員函數
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;
} [2] 
參考資料
  • 1.    AdamDrozdek.C++數據結構與算法:清華大學出版社,2014
  • 2.    陳策 張素卿..C/C++程序設計: 科學出版社,2008