在C++中,運算符和函數是等價的,它和函數一樣可以通過重載的方式來靈活地解決各種實際問題。
運算符重載的格式
運算符重載有兩種形式,一是重載爲成員函數形式,二是重載爲友元(或普通的類外)函數形式。
以Complex複數類的 “+” 運算符爲例,重載爲成員函數的形式爲:
class Complex{
int real,image;
public:
Complex(int r=0,int i=0):real(r),image(i){};
Complex operator+(const Complex& c);//成員函數形式重載"+"
};
Complex Complex::operator+(const Complex& c){ //具體實現
return Complex(real+c.real,image+c.image);
}
重載爲友元函數的形式爲:
class Complex{
int real,image;
public:
Complex(int r=0,int i=0):real(r),image(i){};
friend Complex operator+(const Complex& c,const Complex& c2);//友元函數形式重載"+"
};
Complex operator+(const Complex& c,const Complex& c2){ //具體實現
return Complex(c.real+c2.real,c.image+c2.image);
}
運算符重載比函數重載多了一個operator關鍵字,重載爲成員函數時有一個參數爲類對象本身,而重載爲外部函數(友元或者普通函數)時需要將所有參數寫出來。
返回類型 operator 運算符(參數列表){
}
或者重載爲成員函數形式:
返回類型 所在類的類名::operator 運算符(參數列表){
}
其中參數列表根據重載的運算符定,例如二元運算符重載爲友元函數時需要兩個參數(兩個操作數),重載爲成員函數時需要一個參數(另一個參數是該類對象本身)。
運算符重載的規則
1. C++中不可重載的運算符有:
“ . ” 成員訪問運算符
“ .* ” 和 “ ->* ” 成員指針訪問運算符
“ :: ” 域運算符
“ sizeof ” 長度運算符
“ ?: ” 條件運算符
“ # ” 預處理符號
其餘的運算符皆可以重載,如(包括但不限於):
雙目算術運算符 + (加),-(減),*(乘),/(除),% (取模)
關係運算符 ==(等於),!= (不等於),< (小於),> (大於>,<=(小於等於),>=(大於等於)
邏輯運算符 ||(邏輯或),&&(邏輯與),!(邏輯非)
單目運算符 + (正),-(負),*(指針),&(取地址)
自增自減運算符 ++(自增),--(自減)
位運算符 | (按位或),& (按位與),~(按位取反),^(按位異或),,<< (左移),>>(右移)
賦值運算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空間申請與釋放 new, delete, new[ ] , delete[]
其他運算符 ()(函數調用),->(成員訪問),,(逗號),[](下標)
2.C++規定 “=”、“[]”、“()”、“->” 運算符只能以成員函數形式重載,不能重載爲友元函數
3.重載運算符不可改變其優先級、不可改變其操作數個數、不可臆造新運算符。
4.運算符重載只能和自定義類型一起使用,即參數裏至少要有一個是類對象,例如試圖重載整數運算的“ + ”是不被允許的:
int operator + (int a,int b)
{
return a-b;
}
5.除上述特別說明的運算符之外的運算符,可以重載爲類的成員函數、友元函數、普通的類外函數,不可重載爲靜態函數
運算符重載的調用方式
以上文重載的Complex類的“+”運算符爲例,聲明對象c1和c2,並且讓它們相加:
Complex c1(1,2),c2(3,4);
Complex c3 = c1 + c2;
其中"c1 + c2"會被編譯器處理成“c1.operator+(c2)”的形式,如果是重載爲友元函數形式就會被處理成“operator+(c1,c2)”.如果我們重載一元運算符“-”(一元的“-”意爲取負,不是相減運算符):
class Complex{
int real,image;
public:
Complex(int r,int i):real(r),image(i){};
friend Complex operator+(const Complex& c,const Complex& c2);//友元函數形式重載"+"
Complex operator-();//友元形式重載取負運算符
};
Complex operator+(const Complex& c,const Complex& c2){ //具體實現
return Complex(c.real+c2.real,c.image+c2.image);
}
Complex Complex::operator-(){//具體實現
return Complex(-real,-image);
}
int main(){
Complex c1(1,2);
Complex c3 = -c1;//注意這裏
return 0;
}
則“-c1”會被處理成“c1.operator-()”,友元函數形式則是“operator-(c1)”。
自增自減運算符的重載
那麼自增和自減運算符又怎樣重載?我們以自增爲例,自減同理。自增又分爲前置自增和後置自增,C++規定前置自增重載形式爲“operator++(void)”後置自增重載爲“operator++(int)”,這是成員函數重載形式,友元函數重載則是“operator++(const Complex& c)”和“operator++(const Complex& c,int)”這兩種寫法分別是前置和後置自增運算符的重載寫法,後置++使用一個int參數來與前置++區分。
下面給出重載爲成員函數的自增運算符寫法,請注意對比前置++和後置++的實現和返回值類型:
#include <iostream>
using namespace std;
class Complex{
int real,image;
public:
Complex(int r=0,int i=0):real(r),image(i){};
friend Complex operator+(const Complex& c,const Complex& c2);//友元函數形式重載"+"
Complex operator-();//友元形式重載取負運算符
Complex& operator++();//前置++
Complex operator++(int);//後置++
void show();//測試用,輸出整個虛數
};
void Complex::show(){
cout << real
<< " + "
<< image
<< "i"
<< endl;
}
Complex operator+(const Complex& c,const Complex& c2){ //具體實現
return Complex(c.real+c2.real,c.image+c2.image);
}
Complex Complex::operator-(){//具體實現
return Complex(-real,-image);
}
Complex& Complex::operator++(){//前置++實現
cout << "前置++"
<< endl;
real++;
image++;
return *this;
}
Complex Complex::operator++(int){//後置++實現
cout << "後置++"
<< endl;
Complex tmp = *this;
real++;
image++;
return tmp;
}
int main(){
Complex c;
c.show();
//測試前置++
++c;
c.show();
//測試後置++
Complex c2 = c++;
c.show();
c2.show();
return 0;
}
習慣上來說,前置++應該返回一個左值,我們應該返回原對象的引用,而後置++則會返回一個未修改前的副本,我們應該直接返回一個對象,讓C++去創建副本返回。類比C++本身的自增運算符,例如對一個int變量分別使用前置++和後置++,我們可以知道前置++是可以作爲左值的,而後置++是不可以作爲左值的(後置++返回的是一個臨時變量,是不可尋址的,所以不能其賦值),我們設計Complex類的時候也應該考慮到這些問題。
其他運算符重載的細節問題
首先要注意的是,我們在重載運算符時應該仔細斟酌返回值類型,例如在重載“=”時,應該返回賦值後的對象的引用,這樣就可以實現連續賦值。“=”運算符系統會幫我們自動生成,當類成員裏有指針的時候,系統實現的 “=”只會幫我們把所有成員變量原封不動地賦值,賦值完畢後,“=”左右兩邊的對象都指向同一塊內存,當其中一個對象先銷燬,另一個對象再銷燬的時候就會造成重複釋放內存的問題,遇到這種情況我們應該自己手動實現一個“=”的重載,爲新對象的指針申請獨立的內存,並把內容拷貝過來。此外,還要注意“c1 = c1”這種寫法,在處理“=”運算符時我們應該先判斷一下“=”左右兩邊的對象是否相等,相等就直接返回原對象的引用。還要注意的是,在重載“=”運算符的時候,如果對象本身帶有指向用new申請的內存的指針成員變量,要注意“被覆蓋的對象 的成員指針內存的銷燬問題”,這句話聽起來好像有點難懂,其實就是當我們寫出c1 = c2這樣的代碼時,c1對象的成員變量裏如果帶有用new申請內存的指針變量的話,要注意先釋放再賦值,否則會導致內存泄露。
到此,運算符重載的知識點就總結得差不多了,本文就寫到這裏,感謝您的閱讀,如果文中有什麼錯誤或者難以理解的地方,歡迎各位在評論區留言。