重載操作符是個好青年,但是要吐槽的是 ,我們時常為了重載操作符編寫許多重復的代碼。這是枯燥的,但是也是必須的。你重載的越多,你的類的彈性就越大。但是,你也不能為所欲為。玩游戲總是遵守相應的規則,寫重載操作符亦是如此 !
以下是要遵守的游戲規則 :
• 一元操作符可以是不帶參數的成員函數或帶一個參數的非成員函數。
• 二元操作符可以是帶一個參數的成員函數或帶兩個參數的非成員函數。
• operator=、operator[]、operator()、operator->只能定義為成員函數。
• operator->的返回值必須是一個指針或能使用->的對象。
• 重載 operator++ 和 operator--時帶一個 int 參數表示後綴,不帶參數表示前綴。
• 除 operator new 和 operator delete 外,重載的操作符參數中至少要有一個非內建數據類型。
• 重載的的操作符應盡量模擬操作符對內建類型的行為。
在看完游戲規則之後,我們就各個部分的實現和注意點進行肢解 。
⒈ 輸入和輸出操作符的重載
對於>>和<<的重載要注意以下幾個要點 :
① IO操作符必須為 非成員函數,如果將其定義為成員函數,那麼IO操作符的操作習慣將和正常的習慣相反。多怪啊!此時,你或許會問,那我怎麼調用對象中的私有成員呢?別急,我們不是有友員和友類嗎?將IO操作符重載函數定義成類的友員函數,這個問題就迎刃而解了 。
② 在輸入期間,我們可能會碰到錯誤。此時,要恢復對象為初始狀態。也就是,在輸入之前什麼樣子,我們就恢復成那個樣子。
③ 這個要點是摘至《C++ primer》,我覺得挺好的。我們可以重載操作符,意味著我們自由的空間就大了。但是,我們不要忘了IO操作符的本質,不要過度的格式化,應將格式化降到最低。
在注意了幾個要點之後,我們看一個完整的IO操作符的實現例子:
#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){} // 利用初始化列表來初始化成員對象
friend ostream &operator<<(ostream &stream, MyClass o); // 操作符被定義為非成員函數時,要將其定義為所操作類的友員
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); // 相當於operator>>(cin, a)
cout << a; // 相當於operator<<(cout, a)
return 0;
}
我覺得,許多的事情都是盡在不言中。看了代碼,你就知道,這個家伙是這麼用的,這樣用才是規范的。好了接下來介紹算術操作符和關系操作符。
⒉ 算術操作符和關系操作符的重載
一般而言,將算術操作符和關系操作符定義為非成員函數。
① 算術操作符
那就看代碼怎麼實現吧:
#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); // 兩個對象相加
friend Point operator+(int value, Point &p1); // 對象和值的相加
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.2cto.com
首先,“==”相等操作符的兩個對象包含相同的數據,這樣才有比較性。其次,定義了operator==,同時也要定義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);
}
⒊ 賦值操作符
賦值操作符有個強調點,那是賦值必須返回對 *this的引用。要定義為成員函數。
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;
}
⒋ 下標操作符
可以從容器中檢索單個元素的容器類一般會定義下標操作符operator[]。首先,要注意到, 下標操作符必須定義為成員函數。其次,要定義兩個版本,一個是非const成員並返回引用。一個是為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中,可以看到重載的情形:(夠簡潔的 )
reference operator[](size_type __n) { return *(begin() + __n); }
const_reference operator[](size_type __n) const { return *(begin() + __n); }
⒌ 成員訪問操作符
C++中支持重載解引用操作符(*)和箭頭操作符(->),其中, 箭頭操作符必須定義為類成員函數,解引用則兩者皆可。看看以下的用法:
_Reference operator*() const {
_BidirectionalIterator __tmp = current;
return *--__tmp; // 返回值
}
pointer operator->() const { return &(operator*()); } // 返回指針
⒍ 自增和自減操作
a++,++a,--b,b--。是不是有點煩人?但是看了重載的意義之後,你就知道,這個東西是不煩人的。也知道了在for循環中為什麼要強調用++a了。
在C++中,並沒有特別要求說一定要為成員函數,但是為成員函數是一個不錯的選擇 。
還有要注意的是 :
① 為了與內置類型一致,前綴式操作符應返回被增量或減量對象的引用;
② 後綴式返回舊值,應作為值返回,不是返回引用,所以返回不用引用。
現在看看如何使用:
Point &operator++(); // 為了與內置類型一致,前綴式操作符應返回被增量或減量對象的引用
Point operator++(int); // 返回舊值,應作為值返回,不是返回引用
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;
}
知道為什麼說要強調說前綴式了嗎 ?看看前綴和後綴的區別,你就知道那個效率會高。。。
總結,這些東西寫的有點辛苦,寫寫停停的。請大牛指正其中不妥之處,小菜謝謝了。。。
摘自 雲端小飛象cg