閱讀905 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Way on c & c++ 小記 [七] – 重載操作符

重載操作符

作者:Jason Lee @https://blog.csdn.net/jasonblog

日期:2010-04-17

 

[1]重載操作符

重載操作符從大的方麵來講可以分為兩類:最好或必須作為類的成員函數的,以及相反。而具體地講,最好或必須作為類的成員函數的有賦值操作符( = )、下標操作符( [] )、調用操作符( () )、成員訪問箭頭操作符( ->,目前列出的操作符都必須為成員函數) 、星號解引用操作符( * )、複合賦值操作符( +=)、自增、自減。其它的一些操作符,如算術操作符、相等操作符、關係操作符和位操作符,最好定義為非成員函數,在這種情況下,通常需要將其定義為類的友元函數。當然,還有一些是不建議重載的操作符,如逗號、取地址運算符和邏輯運算符等。

 

[2]賦值操作符

賦值操作符必須是類的成員函數,因為編譯器需要知道類是否有賦值操作符這個信息。並且,賦值操作符必須返回對*this的引用,也就是左操作數(對象自身)的引用。

同樣的,複合賦值操作符也應返回對*this的引用。

如下是一段示例代碼:

#include <iostream> using namespace std; class Demo { public: Demo():val(0){} Demo(int t): val(t){} Demo(const Demo &demo){ val = demo.val; } ~Demo(){} Demo& operator=(const Demo &demo){ val = demo.val; return *this; } Demo& operator+=(const Demo &demo){ val += demo.val; return *this; } void showVal(){ cout << val << endl; } private: int val; }; int main(){ Demo d1 = 2;// 首先調用接受整型參數的構造函數創建一個臨時對象,再調用複製構造函數 d1.showVal(); Demo d2; d2 += d1;// 使用複合賦值操作符 d2.showVal(); return 0; }

通常定義了賦值操作符,那麼接著定義複製構造函數和複合賦值操作符是比較合理的。接著又為了體現複製構造函數的運用,直接在實例化 d1 的時候使用了 Demo d1 = 2; 這樣的語句,就類似 string str = “hello”; 先調用對應參數的構造函數創建臨時對象再調用複製構造函數。

 

[3] 下標操作符

下標操作符也必須定義為類的成員函數。並且,下標操作符有個需要注意的問題是,當它出現在賦值操作符的任意一邊時都應該能正常工作,所以下標操作符應該返回引用,這樣才能得到左值,使得下標操作符可以出現在賦值操作符的任意一邊。

可以使用下標操作符還保證不非法越界:

#include <iostream> using namespace std; class Demo { public: Demo(): flag(false){} Demo(int sz): flag(true), size(sz){ p = new int[sz]; } ~Demo(){if(flag) delete []p;} int& operator[](const int index){ if(flag){ if(index >= size){/* 非法越界處理代碼 */} else return p[index]; } } private: bool flag;// 需要有個標誌判斷是否有為 p 非配空間,避免非法訪存 int *p; int size; }; int main(){ Demo d1(3); d1[0] = 1; int t = d1[0]; return 0; }

 

[4] 箭頭和星號操作符

箭頭操作符必須定義為類成員函數,而星號操作符則無此要求。有了這兩種操作符可以重載,就可以使類表現得像指針一樣,或者也可以稱其為指針型的類,由此可以實現如 smart pointer 這種雖然號稱智能指針但也是智能得有限的類。而 STL 中的迭代器就是一個典型的應用:

#include <iostream> #include <vector> using namespace std; int main () { vector<int> myvector; for (int i=1; i<=5; i++) myvector.push_back(i); vector<int>::iterator it; cout << "myvector contains:"; for ( it=myvector.begin() ; it < myvector.end(); it++ ) cout << " " << *it; cout << endl; return 0; }

 

[5] 算術操作符和關係操作符

算術操作符和關係操作符一般應定義為非成員函數。其中為了保持與內置操作符一致,加法不返回引用。並且,如果可以的話,使用複合賦值操作符來實現算術操作符會更有效率。

相等操作符和不等操作符一般也是相生的,因為需要其一時往往需要另一,並且往往其中一個操作符是調用另一個操作符實現的。

而當使用的容器運作於某些算法需要關係操作符時,如小於操作符,定義該種關係操作符往往會使得代碼更加有效率以及簡潔。

 

[6] 自增、自減操作符

自增、自減操作符的重載可以使得一個類表現得如整型一般,從而可以作為迭代器,並且又分為前綴和後綴兩種運算。

上一段代碼(關於星號和箭頭操作符)中就有使用到重載自增操作符。通常這種應用是通過 3 個指針來實現的,一個是 begin ,一個是 current ,還有一個是 end 。當然名稱不一定如此。首先使用 begin 指針確定迭代開始的初始位置,並使用 end 指針限定範圍,最後通過 current 指針遍曆元素。

為了與內置類型一致,或者說為了保持習慣用法,前綴式操作符應該返回發生改變(增或減)後的對象的引用,而後綴式操作符應該返回舊值。

另外,為了區分前綴式操作符和後綴式操作符,指定了後綴式操作符函數接收一個無用的 int 型形參,形如 operator++(int) 表示後綴式操作符,而 operator++() 表示前綴式操作符。

 

[7] 輸入輸出操作符

上麵提了許多,但想來最常用的重載操作符可能是輸入輸出操作符,並且這二者最好定義為非成員函數,使其符合使用標準。

從標準使用的角度來講,輸出操作符應該接受 ostream& 作為第一個形參,並返回對該形參的引用:

#include <iostream> using namespace std; class Demo { public: Demo(): p(0){} ~Demo(){} friend ostream& operator<<(ostream &os, const Demo &demo); private: int p; }; ostream& operator<<(ostream &os, const Demo &demo){ os << demo.p; return os; } int main(){ Demo d1; cout << d1 << endl; return 0; }

而輸入操作符也具有相同模式:接受 istream& 參數作為第一形參並返回該形參的引用。此外,輸入操作符還需要注意的是讀入過程的錯誤處理。

 

[8] 調用操作符

最後一個提及的重載操作符是調用操作符,它必需作為成員函數,而且因為作用同函數類似,所以具有調用操作符的類經實例化而得的對象也被稱為函數對象( function object )。

#include <iostream> using namespace std; class Demo { public: int operator()(int m, int n){ return m>n ? m:n; } }; int main(){ Demo d1; cout << d1(2,3) << endl; return 0; }

如上,使用的是 Demo 類的調用運算符,功能就好像一個返回較大值的函數。

 

最後更新:2017-04-02 05:21:04

  上一篇:go magento開發 -- 再說Magento時間輸出問題
  下一篇:go eBay是怎樣練成的