對於某些運算符號(+,-,*,/....),我們不滿足與它原有的操作方式,而是要在對特有對象(如負數的加減)進行使用,但是編譯器會不允許這樣做,因為會與操作符原本的類型不匹配而導致操作失敗。因此我們需要對運算符進行重載,即賦予它新的意義,從而滿足我們的使用需求。 如complex_a和complex ...
對於某些運算符號(+,-,*,/....),我們不滿足與它原有的操作方式,而是要在對特有對象(如負數的加減)進行使用,但是編譯器會不允許這樣做,因為會與操作符原本的類型不匹配而導致操作失敗。因此我們需要對運算符進行重載,即賦予它新的意義,從而滿足我們的使用需求。
如complex_a和complex_b是兩個複數對象求兩個複數的和, 希望能直接寫:complexa + comple_b
運算符重載的目的是:擴展C++中提供的運算符的適用範圍,使之能作用於對象。
同一個運算符,對不同類型的操作數,所發生的行為不同。
對於複數對象:complex_a + complex_b => 生成新的複數對象
對於整數:5 + 4 = 9
運算符重載的實質是函數重載,它可以重載為普通函數,也可以重載為成員,在對含有該運演算法的表達式轉換時,調用對應的運算符函數完成重載的操作。(依據參數的類型進行匹配)
1 class Complex
2 {
3 public:double real,imag;
4 Complex( double r = 0.0, double i= 0.0 ):real(r),imag(i) {}
5 Complex operator-(const Complex & c);
6 };
7 Complex operator + ( const Complex & a, const Complex & b)
8 {
9 return Complex( a.real+b.real,a.imag+b.imag); //返回一個臨時對象
10 }
11 Complex Complex::operator-(const Complex & c)
12 {
13 return Complex(real - c.real, imag - c.imag); //返回一個臨時對象
14 }
15 //重載為成員函數時,參數個數為運算符目數減一。
16 //重載為普通函數時,參數個數為運算符目數。
17 int main()
18 {
19 Complex a(4,4),b(1,1),c;
20 c = a + b; //等價於c=operator+(a,b)
21 cout << c.real << "," << c.imag << endl;//5,5,
22 cout << (a-b).real << "," << (a-b).imag << endl;//3,3
23 //a-b等價於a.operator-(b)
24 return 0;
25 }
- 賦值運算符重載
有時候希望賦值運算符兩邊的類型可以不匹配,比如,把一個int類型變數賦值給一個Complex對象,把一個 char * 類型的字元串賦值給一個字元串對象,此時就需要重載賦值運算符“=”。賦值運算符“=”只能重載為成員函數
1 class String
2 {
3 private:
4 char * str;
5 public:
6 String ():str(new char[1]) { str[0] = 0;}
7 const char * c_str() { return str; };
8 String & operator = (const char * s);
9 String::~String( ) { delete [] str; }
10 };
11 String & String::operator = (const char * s)
12 { //重載“=”以使得 obj = “hello”能夠成立
13 delete [] str;
14 str = new char[strlen(s)+1];
15 strcpy( str, s);
16 return * this;
17 }
18 int main()
19 {
20 String s;
21 s = "Good Luck," ; //等價於 s.operator=("Good Luck,");
22 cout << s.c_str() << endl;
23 // String s2 = "hello!"; //error
24 s = "Shenzhou 8!"; //等價於 s.operator=("Shenzhou 8!");
25 cout << s.c_str() << endl;
26 return 0;
27 }
28 // 輸出:
29 // Good Luck,
30 // Shenzhou 8!
- 淺拷貝和深拷貝
一個字元串的例子
1 String S1, S2;
2 S1 = “this”;
3 S2 = “that”;
4 S1 = S2;
這幾句的含義很清楚,一個S1串和S2串,並將S2賦值給S1,此時S1就和S2是一樣的值。。但是....這樣卻不是我們理解的那種copy
它是在將s2賦值給s1的時候,將原來指向s1的指針取指向s2
原本指向'this'字元串的指針s1指向了'that'的字元空間,這樣原來的'this'的字元空間就找不到了(變成記憶體垃圾)。
這就是淺拷貝。此時若我們釋放s1所指向的存儲空間,將會釋放掉'that',但是繼續釋放s2時,會發生問題(程式崩潰),因為此刻s2指向的存儲空間已經被s1所釋放了
但是原來的'this'卻孤獨的無人問津,好慘 ...所以這樣是很不妥的。。。或者,我們更換此刻s1的值,又導致'that'被更換了。。。就會一團糟。。
所以需要對原來的'='號進行重載(深拷貝:生成一個新的,值與當前的值一樣的,不同地址空間的複製,互相的操作不相往來的那種,即真正意義上的copy)
1 class String
2 {
3 private:
4 char * str;
5 public:
6 String ():str(new char[1])
7 {
8 str[0] = 0;
9 }
10 const char * c_str()
11 {
12 return str;
13 };
14 String & operator = (const char * s)
15 {
16 delete [] str;//刪除原來的str
17 str = new char[strlen(s)+1];//開闢足夠大的存儲空間
18 strcpy( str, s);//將s拷貝過來
19 return * this;//這樣s1就會指向新分配的存儲空間
20 };
21 ~String( )
22 {
23 delete [] str;
24 }
25 };
但是,這樣寫還是有一點小問題的。如果我寫了這個 s=s,就問又有小問題。這樣在賦值時,左邊的s先被delete,然後將右邊s賦值給左邊的s,但是右邊的s已經沒了啊?!?!?這樣就出錯了。所以代碼要繼續修改,成這樣
1 String & operator = (const String & s)
2 {
3 if( this == & s) //
4 return * this;//防止出現自己給自己賦值出錯的情況,直接返回就行了
5 delete [] str;
6 str = new char[strlen(s.str)+1];
7 strcpy( str,s.str);
8 return * this;
9 }
註意,返回值類型是String & 型這樣是為了對應原來 “=”運算符的左右兩邊類型
1 //如
2 a = b = c;
3 (a=b)=c; //會修改a的值
4 //分別等價於:
5 a.operator= (b.operator=(c));
6 (a.operator=(b)).operator=(c);
現在應該可以了。
但是。。。。
1 //為 String類編寫複製構造函數的時候,會面臨和不重載的 = 同樣的問題,即預設構造函數會將=變成複製的操作,是淺拷貝!!所以我們用同樣的方法處理。寫個複製構造
2 String( String & s)
3 {
4 str = new char[strlen(s.str)+1];
5 strcpy(str,s.str)
6 }