重載操作符是個(gè)好青年,但是要吐槽的是 ,我們時(shí)常為了重載操作符編寫許多重復(fù)的代碼。這是枯燥的,但是也是必須的。你重載的越多,你的類的彈性就越大。但是,你也不能為所欲為。玩游戲總是遵守相應(yīng)的規(guī)則,寫重載操作符亦是如此 ! 以下是要遵守的游戲規(guī)則 : 一元操作符可以是不帶參數(shù)的成員函數(shù)或帶一個(gè)參數(shù)的非成員函數(shù)。 二元操作符可以是帶一個(gè)參數(shù)的成員函數(shù)或帶兩個(gè)參數(shù)的非成員函數(shù)。 operator=、operator[]、operator()、operator->只能定義為成員函數(shù)。 operator->的返回值必須是一個(gè)指針或能使用->的對(duì)象。 重載 operator++ 和 operator--時(shí)帶一個(gè) int 參數(shù)表示后綴,不帶參數(shù)表示前綴。 除 operator new 和 operator delete 外,重載的操作符參數(shù)中至少要有一個(gè)非內(nèi)建數(shù)據(jù)類型。 重載的的操作符應(yīng)盡量模擬操作符對(duì)內(nèi)建類型的行為。 在看完游戲規(guī)則之后,我們就各個(gè)部分的實(shí)現(xiàn)和注意點(diǎn)進(jìn)行肢解 。 ⒈ 輸入和輸出操作符的重載 對(duì)于>>和<<的重載要注意以下幾個(gè)要點(diǎn) : ① IO操作符必須為 非成員函數(shù),如果將其定義為成員函數(shù),那么IO操作符的操作習(xí)慣將和正常的習(xí)慣相反。多怪??!此時(shí),你或許會(huì)問,那我怎么調(diào)用對(duì)象中的私有成員呢?別急,我們不是有友員和友類嗎?將IO操作符重載函數(shù)定義成類的友員函數(shù),這個(gè)問題就迎刃而解了 。 ② 在輸入期間,我們可能會(huì)碰到錯(cuò)誤。此時(shí),要恢復(fù)對(duì)象為初始狀態(tài)。也就是,在輸入之前什么樣子,我們就恢復(fù)成那個(gè)樣子。 ③ 這個(gè)要點(diǎn)是摘至《C++ primer》,我覺得挺好的。我們可以重載操作符,意味著我們自由的空間就大了。但是,我們不要忘了IO操作符的本質(zhì),不要過度的格式化,應(yīng)將格式化降到最低。 在注意了幾個(gè)要點(diǎn)之后,我們看一個(gè)完整的IO操作符的實(shí)現(xiàn)例子: #include <iostream> #include <string> using namespace std; class MyClass { private: string name; int id; int prefix; int value; public: MyClass() { }; MyClass(string n, int a, int p, int nm):name(n), id(a), prefix(p), value(nm){} // 利用初始化列表來初始化成員對(duì)象 friend ostream &operator<<(ostream &stream, MyClass o); // 操作符被定義為非成員函數(shù)時(shí),要將其定義為所操作類的友員 friend istream &operator>>(istream &stream, MyClass &o); }; ostream &operator<<(ostream &stream, MyClass o) { stream << o.name << " "; stream << "(" << o.id << ") "; stream << o.prefix << "-" << o.value << "\n"; return stream; } istream &operator>>(istream &stream, MyClass &o) { cout << "Enter name: "; stream >> o.name; cout << "Enter id: "; stream >> o.id; cout << "Enter prefix: "; stream >> o.prefix; cout << "Enter value: "; stream >> o.value; cout << endl; return stream; } int main() { MyClass a; operator>>(cin, a); // 相當(dāng)于operator>>(cin, a) cout << a; // 相當(dāng)于operator<<(cout, a) return 0; } 我覺得,許多的事情都是盡在不言中??戳舜a,你就知道,這個(gè)家伙是這么用的,這樣用才是規(guī)范的。好了接下來介紹算術(shù)操作符和關(guān)系操作符。 ⒉ 算術(shù)操作符和關(guān)系操作符的重載 一般而言,將算術(shù)操作符和關(guān)系操作符定義為非成員函數(shù)。 ① 算術(shù)操作符 那就看代碼怎么實(shí)現(xiàn)吧: #include <iostream> #include <string> using namespace std; class Point { public: Point(){}; Point(int x_, int y_):x(x_),y(y_){}; Point(const Point &p){ this->x = p.x; this->y = p.y; }; ~Point(){}; friend Point operator+(Point &p1, Point &p2); // 兩個(gè)對(duì)象相加 friend Point operator+(int value, Point &p1); // 對(duì)象和值的相加 friend ostream &operator<<(ostream &os, Point &p1); private: int x; int y; }; Point operator+(Point &p1, Point &p2) { Point temp; temp.x = p1.x + p2.x; temp.y = p1.y + p2.y; return temp; } Point operator+(int value, Point &p1) { Point temp; temp.x = p1.x + value; temp.y = p1.y + value; return temp; } ostream &operator<<(ostream &os, Point &p1) { os << p1.x << " " << p1.y << endl; return os; } int main() { Point p1(1,2); Point p2(3,4); cout << p1 + p2; cout << 5 + p1; return 0; } ② 相等操作符www. 首先,“==”相等操作符的兩個(gè)對(duì)象包含相同的數(shù)據(jù),這樣才有比較性。其次,定義了operator==,同時(shí)也要定義operator!=。 friend bool operator==(Point &p1, Point &p2); friend bool operator!=(Point &p1, Point &p2); ....... bool operator==(Point &p1, Point &p2) { return (p1.x == p2.x)&&(p1.y == p2.y); } bool operator!=(Point &p1, Point &p2) { return !(p1 == p2); } ⒊ 賦值操作符 賦值操作符有個(gè)強(qiáng)調(diào)點(diǎn),那是賦值必須返回對(duì) *this的引用。要定義為成員函數(shù)。 Point &operator=(const Point &p1); Point &operator+=(const Point &p1); ..... Point &Point::operator=(const Point &p1) { this->x = p1.x; this->y = p1.y; return *this; } Point &Point::operator+=(const Point &p1) { this->x += p1.x; this->y += p1.y; return *this; } ⒋ 下標(biāo)操作符 可以從容器中檢索單個(gè)元素的容器類一般會(huì)定義下標(biāo)操作符operator[]。首先,要注意到, 下標(biāo)操作符必須定義為成員函數(shù)。其次,要定義兩個(gè)版本,一個(gè)是非const成員并返回引用。一個(gè)是為const成員并返回引用。 #include <iostream> using namespace std; class Point { int a[3]; public: Point(int i, int j, int k) { a[0] = i; a[1] = j; a[2] = k; } int &operator[](int &i) { return *(a + i); } const int &operator[](const int &i) { return *(a + i); } }; int main() { Point ob(1, 2, 3); cout << ob[1]; return 0; } 在sgi stl中,可以看到重載的情形:(夠簡(jiǎn)潔的 ) reference operator[](size_type __n) { return *(begin() + __n); } const_reference operator[](size_type __n) const { return *(begin() + __n); } ⒌ 成員訪問操作符 C++中支持重載解引用操作符(*)和箭頭操作符(->),其中, 箭頭操作符必須定義為類成員函數(shù),解引用則兩者皆可。看看以下的用法: _Reference operator*() const { _BidirectionalIterator __tmp = current; return *--__tmp; // 返回值 } pointer operator->() const { return &(operator*()); } // 返回指針 ⒍ 自增和自減操作 a++,++a,--b,b--。是不是有點(diǎn)煩人?但是看了重載的意義之后,你就知道,這個(gè)東西是不煩人的。也知道了在for循環(huán)中為什么要強(qiáng)調(diào)用++a了。 在C++中,并沒有特別要求說一定要為成員函數(shù),但是為成員函數(shù)是一個(gè)不錯(cuò)的選擇 。 還有要注意的是 : ① 為了與內(nèi)置類型一致,前綴式操作符應(yīng)返回被增量或減量對(duì)象的引用; ② 后綴式返回舊值,應(yīng)作為值返回,不是返回引用,所以返回不用引用。 現(xiàn)在看看如何使用: Point &operator++(); // 為了與內(nèi)置類型一致,前綴式操作符應(yīng)返回被增量或減量對(duì)象的引用 Point operator++(int); // 返回舊值,應(yīng)作為值返回,不是返回引用 Point &operator--(); Point operator--(int); .... // 前增 Point &Point::operator++() { ++this->x; ++this->y; return *this; } // 后增 Point Point::operator++(int) { Point temp = *this; ++this->x; ++this->y; return temp; } // 前減 Point &Point::operator--() { --this->x; --this->y; return *this; } // 后減 Point Point::operator--(int) { Point temp = *this; --this->x; --this->y; return temp; } 知道為什么說要強(qiáng)調(diào)說前綴式了嗎 ?看看前綴和后綴的區(qū)別,你就知道那個(gè)效率會(huì)高。。。 總結(jié),這些東西寫的有點(diǎn)辛苦,寫寫停停的。請(qǐng)大牛指正其中不妥之處,小菜謝謝了。。。 摘自 云端小飛象cg |
|