1. 程式人生 > >C++ 面向物件- -運算子的過載(二)

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;
}