C++的流插入運算子“<<”和流提取運算子“>>”是C++在類庫中提供的,所有C++編譯系統都在類庫中提供輸入資料流類istream和輸出資料流類ostream。cin和cout分別是istream類和ostream類的對象。在類庫提供的標頭檔中已經對“<<”和“>>”進行了重載,使之作為流插入運算子和流提取運算子,能用來輸出和輸入C++標準類型的資料。因此,凡是用“cout<<”和“cin>>”對標準類型資料進行輸入輸出的,都要用#include 把標頭檔包含到本程式檔案中。
使用者自己定義的類型的資料,是不能直接用“<<”和“>>”來輸出和輸入的。如果想用它們輸出和輸入自己聲明的類型的資料,必須對它們重載。
對“<<”和“>>”重載的函數形式如下:
istream & operator >> (istream &, 自訂類 &); ostream & operator << (ostream &, 自訂類 &);
即重載運算子“>>”的函數的第一個參數和函數的類型都必須是istream&類型,第二個參數是要進行輸入操作的類。重載“<<”的函數的第一個參數和函數的類型都必須是ostream&類型,第二個參數是要進行輸出操作的類。因此,只能將重載“>>”和“<<”的函數作為友元函數或普通的函數,而不能將它們定義為成員函數。
重載流插入運算子“<<”
在程式中,人們希望能用插入運算子“<<”來輸出使用者自己聲明的類的對象的資訊,這就需要重載流插入運算子“<<”。
[例] 用重載的“<<”輸出複數。
#include <iostream>using namespace std;class Complex{ public: Complex( ){real=0;imag=0;} Complex(double r,double i){real=r;imag=i;} Complex operator + (Complex &c2); //運算子“+”重載為成員函數 friend ostream& operator << (ostream&,Complex&); //運算子“<<”重載為友元函數 private: double real; double imag;};Complex Complex::operator + (Complex &c2)//定義運算子“+”重載函數{ return Complex(real+c2.real,imag+c2.imag);}ostream& operator << (ostream& output,Complex& c) //定義運算子“<<”重載函數{ output<<"("<<c.real<<"+"<<c.imag<<"i)"<<endl; return output;}int main( ){ Complex c1(2,4),c2(6,10),c3; c3=c1+c2; cout<<c3; return 0;}
注意,在Visual C++ 6.0環境下運行時,需將第一行改為#include <iostream.h>,並刪去第2行,否則編譯不能通過。運行結果為:
可以看到在對運算子“<<”重載後,在程式中用“<<”不僅能輸出標準類型資料,而且可以輸出使用者自己定義的類對象。用“cout<<c3”即能以複數形式輸出複數對象c3的值。形式直觀,可讀性好,便於使用。
下面對怎樣實現運算子多載作一些說明。程式中重載了運算子“<<”,運算子多載函數中的形參output是ostream類對象的引用,形參名output是使用者任意起的。分析main函數最後第二行:
運算子“<<”的左面是cout,前面已提到cout是ostream類對象。“<<”的右面是c3,它是Complex類對象。由於已將運算子“<<”的重載函式宣告為Complex類的友元函數,編譯系統把“cout<<c3”解釋為
即以cout和c3作為實參,調用下面的operator<<函數:
ostream& operator<<(ostream& output,Complex& c) { output<<"("<<c.real<<"+"<<c.imag<<"i)"<<endl; return output; }
調用函數時,形參output成為cout的引用,形參c成為c3的引用。因此調用函數的過程相當於執行:
cout<<″(″<<c3.real<<″+″<<c3.imag<<″i)″<<endl; return cout;
請注意,上一行中的“<<”是C++預定義的流插入符,因為它右側的運算元是字串常量和double類型資料。執行cout語句輸出複數形式的資訊。然後執行return語句。
請思考,return output的作用是什嗎?回答是能連續向輸出資料流插入資訊。output是ostream類的對象,它是實參cout的引用,也就是cout通過傳送地址給output,使它們二者共用同一段儲存單元,或者說output是cout的別名。因此,return output就是return cout,將輸出資料流cout的現狀返回,即保留輸出資料流的現狀。
請問返回到哪裡?剛才是在執行
在已知cout<<c3的傳回值是cout的當前值。如果有以下輸出:
先處理
即
而執行(cout<<c3)得到的結果就是具有新內容的流對象cout,因此,(cout<<c3)<<c2相當於cout(新值)<<c2。運算子“<<”左側是ostream類對象cout,右側是Complex類對象c2,則再次調用運算子“<<”重載函數,接著向輸出資料流插入c2的資料。現在可以理解了為什麼C++規定運算子“<<”重載函數的第一個參數和函數的類型都必須是ostream類型的引用,就是為了返回cout的當前值以便連續輸出。
請讀者注意區分什麼情況下的“<<”是標準類型資料的流插入符,什麼情況下的“<<”是重載的流插入符。如
有底線的是調用重載的流插入符,後面兩個“<<”不是重載的流插入符,因為它的右側不是Complex類對象而是標準類型的資料,是用預定義的流插入符處理的。
還有一點要說明,在本程式中,在Complex類中定義了運算子“<<”重載函數為友元函數,因此只有在輸出Complex類對象時才能使用重載的運算子,對其他類型的對象是無效的。如
cout<<time1; //time1是Time類對象,不能使用用於Complex類的重載運算子
重載流提取運算子“>>”
C++預定義的運算子“>>”的作用是從一個輸入資料流中提取資料,如“cin>>i;”表示從輸入資料流中提取一個整數賦給變數i(假設已定義i為int型)。重載流提取運算子的目的是希望將“>>”用於輸入自訂類型的對象的資訊。
[例] 在上例的基礎上,增加重載流提取運算子“>>”,用“cin>>”輸入複數,用“cout<<”輸出複數。
#include <iostream>using namespace std;class Complex{ public: friend ostream& operator << (ostream&,Complex&); //聲明重載運算子“<<” friend istream& operator >> (istream&,Complex&); //聲明重載運算子“>>” private: double real; double imag;};ostream& operator << (ostream& output,Complex& c) //定義重載運算子“<<”{ output<<"("<<c.real<<"+"<<c.imag<<"i)"; return output;}istream& operator >> (istream& input,Complex& c) //定義重載運算子“>>”{ cout<<"input real part and imaginary part of complex number:"; input>>c.real>>c.imag; return input;}int main( ){ Complex c1,c2; cin>>c1>>c2; cout<<"c1="<<c1<<endl; cout<<"c2="<<c2<<endl; return 0;}
運行情況如下:
input real part and imaginary part of complex number:3 6input real part and imaginary part of complex number:4 10c1=(3+6i)c2=(4+10i)
以上運行結果無疑是正確的,但並不完善。在輸入複數的虛部為正值時,輸出的結果是沒有問題的,但是虛部如果是負數,就不理想,請觀察輸出結果。
input real part and imaginary part of complex number:3 6input real part and imaginary part of complex number:4 -10c1=(3+6i)c2=(4+-10i)
根據先調試通過,最後完善的原則,可對程式作必要的修改。將重載運算子“<<”函數修改如下:
ostream& operator << (ostream& output,Complex& c){ output<<"("<<c.real; if(c.imag>=0) output<<"+";//虛部為正數時,在虛部前加“+”號 output<<c.imag<<"i)"<<endl; //虛部為負數時,在虛部前不加“+”號 return output;}
這樣,運行時輸出的最後一行為c2=(4-10i) 。
可以看到,在C++中,運算子多載是很重要的、很有實用意義的。它使類的設計更加豐富多彩,擴大了類的功能和使用範圍,使程式易於理解,易於對對象進行操作,它體現了為使用者著想、方便使用者使用的思想。有了運算子多載,在聲明了類之後,人們就可以像使用標準類型一樣來使用自己聲明的類。類的聲明往往是一勞永逸的,有了好的類,使用者在程式中就不必定義許多成員函數去完成某些運算和輸入輸出的功能,使主函數更加簡單易讀。好的運算子多載能體現物件導向程式設計思想。
可以看到,在運算子多載中使用引用(reference)的重要性。利用引用作為函數的形參可以在調用函數的過程中不是用傳遞值的方式進行虛實結合,而是通過傳址方式使形參成為實參的別名,因此不產生臨時變數(實參的副本),減少了時間和空間的開銷。此外,如果重載函數的傳回值是對象的引用時,返回的不是常量,而是引用所代表的對象,它可以出現在賦值號的左側而成為左值(left value),可以被賦值或參與其他動作(如保留cout流的當前值以便能連續使用“<<”輸出)。但使用引用時要特別小心,因為修改了引用就等於修改了它所代表的對象。