基本概念
重載的運算符是具有特殊名字的函數:它們的名字由關鍵字operator和其後要定義的運算符號共同組成。和其他函數一樣,重載的運算符也包含返回類型、參數列表以及函數體。
對於一個運算符函數來說,它或者是類的成員,或者至少含有一個類類型的參數
當一個重載的運算符是成員函數時,this綁定到左側運算對象。成員運算符函數的(顯式)參數數量比與運算對象的數量少一個
當我們定義重載的運算符時,必須首先決定是將其聲明爲類的成員函數還是聲明一個普通的非成員函數。在某些時候我們別無選擇,因爲有的運算符作爲普通函數比作爲成員更好,有下面原則來選擇:
- 賦值(=)、下標([ ])、 調用(( ))和成員訪問箭頭(->)運算符必須是成員。
- 複合賦值運算符一般來說應該是成員,但並非必須,這一 點與賦值運算符略有不同。
- 改變對象狀態的運算符或者與給定類型密切相關的運算符,如遞增、遞減和解引用
運算符,通常應該是成員。 - 具有對稱性的運算符可能轉換任意一端的運算對象, 例如算術、相等性、關係和位
運算符等,因此它們通常應該是普通的非成員函數。
當我們把運算符定義成成員函數時,它的左側運算對象必須是運算符所屬類的一個對象,例如:
string s = "world";
string t = s + "!"; //正確
string u = "hi" + s; //錯誤
使用下面聲明的類來解決各種運算符重載
class Test {
public:
friend ostream& operator<<(ostream&, const Test&);
friend istream& operator>>(istream&, Test&);
friend Test operator+(const Test&, const Test&);
Test& operator+=(const Test&);
friend bool operator==(const Test&, const Test&);
friend bool operator!=(const Test&, const Test&);
int& operator[](int n) { return vec[n]; }
const int& operator[](int n) const { return vec[n]; }
Test(){}
Test(int _data1,int _data2) : data1(_data1),data2(_data2) {}
~Test() {}
private:
vector<int> vec;
int data1;
double data2;
};
輸入和輸出運算符
一般聲明爲類的友元函數,便於訪問類的私有函數
重載輸出運算符
ostream& operator<<(ostream& os, const Test& test) {
os << test.data1 << " " << test.data2;
return os;
}
第一個參數是ostream的非常量引用,非常量是因爲向流寫入會改變狀態,引用是IO對象不允許拷貝,第二個參數是要輸出類的對象的常引用。
重載輸入運算符
istream& operator>>(istream& is, Test& test) {
is >> test.data1 >> test.data2;
if (is) {
//...
}
else {
test = Test();
}
return is;
}
重載輸入運算符必須處理輸入可能失敗的情況,而輸出運算符不需要
輸入時的錯誤:
- 當流含有錯誤類型的數據時讀取操作可能失敗。例如在讀取完bookNo後,輸入運
算符假定接下來讀入的是兩個數字數據,一旦輸入的不是數字數據,則讀取操作及
後續對流的其他使用都將失敗。 - 當讀取操作到達文件末尾或者遇到輸入流的其他錯誤時也會失敗。
賦值運算符
Test& Test::operator+=(const Test& test) {
data1 += test.data1;
data2 += test.data2;
return *this;
}
算術和關係運算符
通常情況下,我們把算術和關係運算符定義成非成員函數以允許對左側或右側的運算對象進行轉換,因爲這些運算符一般不需要改變運算對象的狀態,所以形參都是常量的引用。
算數運算符
Test operator+(const Test& test1, const Test& test2) {
Test newTest = test1;
newTest += test2;
return newTest;
}
相等運算符
bool operator==(const Test& test1, const Test& test2) {
return test1.data1 == test2.data1 && test1.data1 == test2.data2;
}
bool operator!=(const Test& test1, const Test& test2) {
return !(test1 == test2);
}
下標運算符
int& operator[](int n) { return vec[n]; }
const int& operator[](int n) const { return vec[n]; }
遞增和遞減運算符
//前置
Test& Test::operator++() {
++data1;
++data2;
return *this;
}
Test& Test::operator--() {
--data1;
--data2;
return *this;
}
//後置
Test Test::operator++(int) {
Test test = *this;
++*this;
return test;
}
Test Test::operator--(int) {
Test test = *this;
--* this;
return test;
}
重載、類型轉換與運算符
類型轉換運算符
operator type() const;
舉個例子:
class SmallInt {
public:
SmallInt(int i = 0): val (i){
if(i<011i>255)
throw std: :out_ of_ range ("Bad SmallInt value") ;
}
operator int() const { return val; }
private :
std: :size_ t val;
};
int main(){
SmallInt si;
int tmp = si+3; //首先將si隱式轉換成int,然後執行整數的加法
}
在實踐中,類很少提供類型轉換運算符。在大多數情況下,如果類型轉換自動發生,用戶可能會感覺比較以外,而不是感覺受到了幫助。
所以C++新標準引入了顯式的類型轉換運算符
class SmallInt{
public:
explicit operator int() const {return val;}
}
int main(){
SmallInt si;
int tmp = static_cast<int>si+3;
}
該規定存在一個例外,即如果表達式被用作條件,則編譯器會將顯式的類型轉換自動應用於它。換句話說,當表達式出現在下列位置時,顯式的類型轉換將被隱式地執行:
- if、while及do語句的條件部分
- for語句頭的條件表達式
- 邏輯非運算符(!)、 邏輯或運算符(11)、 邏輯與運算符(&&) 的運算對象
- 條件運算符(? :) 的條件表達式。