C++ 面向物件- -運算子的過載(二)
目錄
過載雙目運算子
在過載雙目運算子時,不言而喻在函式中應該有兩個引數。前面舉的例子也都是雙目運算子。下面舉一個雙目運算子的應用:
#include <iostream> #include <string.h> using namespace std; class String{ char *p ; public: String(){ p=NULL; } String(char *str){ p=str; } void display(){ cout<<p; } friend bool operator>(String &string1 , String &string2); friend bool operator<(String &string1 , String &string2); friend bool operator==(String &string1 , String &string2); }; bool operator>(String &string1 , String &string2){ if(strcmp(string1.p,string2.p)>0) return true; else return false ; } bool operator<(String &string1 , String &string2){ if(strcmp(string1.p,string2.p)<0) return true; else return false ; } bool operator==(String &string1 , String &string2){ if(strcmp(string1.p,string2.p)==0) return true; else return false ; } void compare(String &string1,String &string2){ if(operator>(string1,string2)==1){ string1.display();cout<<">";string2.display(); } else if(operator<(string1,string2)==1){ string1.display();cout<<"<";string2.display(); } else { //else if(operator==(s1,s2)==1) string1.display();cout<<"<";string2.display(); } cout<<endl; }
注:關於字串的比較,不是比較的長度,而是比較的時候,從字串左邊字元開始,一次比較一個字元,直接出現差異、或者其中一個串結束為止,即長度不能直接決定大小,字串的大小是由左邊開始最前面的字元決定的。
這個比較方法很有值得借鑑的地方,還設有方便函式 compare ,我自己嘗試用其他的方法寫了一下,但沒有寫出來。
過載單目運算子
和雙目運算子一樣,單目運算子只有一個運算元,所以過載運算子函式只有一個引數,如果運算子過載函式作為成員函式,則還可以省略此引數。
下面是一個自增運算子的例子:模擬秒錶
#include <iostream> using namespace std; class Time{ int hour; int min; int sec; public: Time(int h=0,int m=0,int s=0):hour(h),min(m),sec(s){} void display(){ cout<<hour<<":"<<min<<":"<<sec<<endl; } Time operator++(); }; Time Time::operator++(){ if(++sec>=60){ //判斷條件仔細思考一下 sec=sec-60; min++; if(min>=60){ min-=60; hour++; } return *this; //之前講的系統提供 this 指標。 } } int main(){ Time t(16,59,50); for(int i=1;i<=12;i++){ ++t; //注意是前置自加 t.display(); } }
有個小問題:如何區別前置自加(減)和後置自加(減)?C++約定,在自加(減)運算子過載時,增加一個 int 型形參,就是後置自加(減)運算子函式。即:
#include <iostream> using namespace std; class Time{ int hour; int min; int sec; public: Time(int h=0,int m=0,int s=0):hour(h),min(m),sec(s){} void display(){ cout<<hour<<":"<<min<<":"<<sec<<endl; } Time operator++(); //前置自加運算子過載 Time operator++(int); //後置自加運算子過載 }; Time Time::operator++(int){ Time temp(*this); //建立臨時物件 temp sec++; if(sec>=60){ sec-=60; min++; if(min>=60){ min-=60; hour++; } } return temp; //返回的是自加前的物件 //此處返回的*this和temp不一樣影響著下面主函式中的 t1 的值 } Time Time::operator++(){ if(++sec>=60){ sec=sec-60; min++; if(min>=60){ min-=60; hour++; } } return *this; } int main(){ Time t(16,59,50); cout<<"t=";t.display(); ++t; cout<<"++t=";t.display(); Time t1; t1=t++; cout<<"t1=";t1.display(); cout<<"t++=";t.display(); }
多的這個 int 型的引數,只是為了與前置自加運算子過載函式有所區別,此外並沒有任何作用,在定義函式時也不用使用該引數,故可省寫引數名,只需在括號中寫 int 即可。編譯系統在遇到過載後置自加運算子時,會自動呼叫此引數。
過載流插入運算子
使用者自定義的型別的資料(如類物件)是不能直接用 "<<" 和 ">>" 輸入輸出的,如果想用它們輸入輸出自己宣告的型別的資料,必須對它們過載。對 "<<" 過載的函式形式如下:
ostream& operator<<(ostream &,自定義類 &);
過載運算子 “<<” 的函式的第一個引數和函式的型別都必須時 ostream &型別(即ostream類物件的引用),第二個引數是要進行輸入操作的類。最重要的一點是,過載流插入運算子只能作為友元函式,而不能定義為成員函式。
下面是一個輸出複數的例子:
#include <iostream>
using namespace std;
class Complex{
double real;
double imag;
public:
Complex(double r=0,double i=0):real(r),imag(i){}
Complex operator+(Complex a);
friend ostream& operator<<(ostream &,Complex);
};
Complex Complex::operator+(Complex a){
return Complex(real+a.real,imag+a.imag) ;
}
ostream& operator<<(ostream &output,Complex c){
output<<"("<<c.real<<","<<c.imag<<"i)"<<endl;
return output;
}
int main(){
Complex c;
/*Complex c1(1,1),c2(2,2),c3;
c3=c1+c2;*/
cout<<c;
}
可以看到經過過載後,“<<”也可以輸出自己定義的類物件,但注意形式: ostream& operator<<(ostream&,Complex); 寫的時候很容易就忘了加 & 符號。
對這個程式分析一下:運算子過載函式 “operator <<” 中的形參 output 是 ostream 類物件的引用,形參名 output 是自己任意起的。看到主函式中將物件輸出時,由於已將運算子 << 的過載函式宣告為 Complex 類的友元函式,那麼編譯時系統會把 cout << c 解釋為 operator << (cout ,c),即以 cout 和 c 作為實參,呼叫 過載運算子函式 “operator <<” ,呼叫函式時,形參 output 成為實參 cout 的引用。那麼不加 & 引用符號不行嗎?因為在C++語言中,我們是用 cout 進行輸出的,如果不加引用符號,相當於新開闢了一塊空間,而和cout沒有一點聯絡,那又如何進行輸出呢,所以引用符號 & 千萬不能少。
至於最後的 return output 的作用是為了能連續向輸出流插入資訊。output 是ostream 類的物件的引用(即實參 cout 的引用,或者說是 cout 的別名),cout 通過傳遞地址給 output ,使它們二者共享一段儲存單元,因此 return output 就是 return cout ,將輸出流 cout 的現狀返回,即保留輸出流的現狀。如果有以下輸出: cout << c << c1 ;程式先執行 cout<<c ;即 (cout << c )<< c1;而執行(cout<<c)得到的結果就是具有新內容的流物件 cout ,因此 (cout <<c) << c1 相當於 cout(新值) << c1.運算子 “<<” 的左側是 ostream 類物件 cout ,右側是 Complex 類物件 c1 ,則再次呼叫運算子 "<<" 過載函式,接著向輸出流插入 c1 的資料。這也是為什麼 運算子 “<<” 過載函式的第一個引數和函式的型別都必須是 ostream 型別的引用,即使為了返回 cout 的當前值以便連續輸出。
還有一點需要注意,在本程式中,在 Complex 類中定義了運算子 "<<" 過載函式為友元函式,因此只有在輸出 Complex 類物件時才能使用過載的運算子,對其他型別的物件是無效的。
過載流提取運算子
過載流提取運算子和前邊的過載流插入運算子很相似,不過流插入運算子是為了進行輸出,這個函式是為了輸入,其過載函式形式如下:
istream& operator>>(istream& , 自定義類名);
同樣以一個複數類的輸入為例:
#include <iostream>
using namespace std;
class Complex{
double real;
double imag;
public:
// Complex(double r=0,double i=0):real(r),imag(i){}
Complex operator+(Complex );
friend istream& operator>>(istream& ,Complex& );
friend ostream& operator<<(ostream& ,Complex );
};
Complex Complex::operator+(Complex c){
Complex a;
a.real=real+c.real;
a.imag=imag+c.imag;
return a;
}
istream& operator>>(istream& input,Complex& c){
input>>c.real>>c.imag;
return input;
}
ostream& operator<<(ostream& output,Complex c){
output<<"("<<c.real<<"+"<<c.imag<<"i)";
return output;
}
int main(){
// Complex c(1,1);
Complex c,c1,c2;
cin>>c>>c1;
cout<<"c="<<c<<endl;
cout<<"c1="<<c1<<endl;
c2=c+c1;
cout<<c2;
}
同樣在執行 cin >> c>>c1 時,呼叫 “operator >>” 函式,將 cin 的地址傳遞給 input ,input 就是cin 的引用,最後返回 cin 的新值。用 cin 和 ">>" 可以連續從輸入流提取資料給程式中的 Complex 類物件,或者說,用 cin 和 ">>" 可以連續向程式輸入 Complex 類物件的值,在主函式中,每遇到一次 “>>” ,就呼叫一次過載運算子 “>>” 的函式。
該函式可以再完善一下,因為發現如果輸入的虛部是負數,結果就不是我們想要的情況。只需要改一下輸出部分的程式碼,即
ostream& operator<<(ostream& output,Complex c){
output<<"("<<c.real;
if(c.imag>=0)
cout<<"+";
cout<<c.imag<<"i)";
return output;
}