C++ 運算符重載


參考文獻:

  《C++程序設計》

  推薦轉載博文:https://www.cnblogs.com/xiaokang01/p/9166745.html#_label1

.............................................................................................................................................................................

  什么是運算符重載?

  運算符重載的本質是一個函數

  

  運算符重載作用:

  

運算符重載限制:

1) 並不是所有的運算符都可以重載。能夠重載的運算符包括:
+  -  *  /  %  ^  &  |  ~  !  =  <  >  +=  -=  *=  /=  %=  ^=  &=  |=  <<  >>  <<=  >>=  ==  !=  <=  >=  &&  ||  ++  --  ,  ->*  ->  ()  []  new  new[]  delete  delete[]

上述運算符中,[]是下標運算符,()是函數調用運算符。自增自減運算符的前置和后置形式都可以重載。長度運算符sizeof、條件運算符: ?、成員選擇符.和域解析運算符::不能被重載。

2) 重載不能改變運算符的優先級和結合性。假設上一節的 complex 類中重載了+號和*號,並且 c1、c2、c3、c4 都是 complex 類的對象,那么下面的語句:

c4 = c1 + c2 * c3;

等價於:

c4 = c1 + ( c2 * c3 );

乘法的優先級仍然高於加法,並且它們仍然是二元運算符。

3) 重載不會改變運算符的用法,原有有幾個操作數、操作數在左邊還是在右邊,這些都不會改變。例如~號右邊只有一個操作數,+號總是出現在兩個操作數之間,重載后也必須如此。

4) 運算符重載函數不能有默認的參數,否則就改變了運算符操作數的個數,這顯然是錯誤的。

5) 運算符重載函數既可以作為類的成員函數,也可以作為全局函數。

將運算符重載函數作為類的成員函數時,二元運算符的參數只有一個,一元運算符不需要參數。之所以少一個參數,是因為這個參數是隱含的。

例如,上節的 complex 類中重載了加法運算符:

complex operator+(const complex & A) const;

當執行:

c3 = c1 + c2;

會被轉換為:

c3 = c1.operator+(c2);

通過 this 指針隱式的訪問 c1 的成員變量。

將運算符重載函數作為全局函數時,二元操作符就需要兩個參數,一元操作符需要一個參數,而且其中必須有一個參數是對象,好讓編譯器區分這是程序員自定義的運算符,防止程序員修改用於內置類型的運算符的性質。

例如,下面這樣是不對的:

  1. int operator + (int a,int b){
  2. return (a-b);
  3. }

+號原來是對兩個數相加,現在企圖通過重載使它的作用改為兩個數相減, 如果允許這樣重載的話,那么表達式4+3的結果是 7 還是 1 呢?顯然,這是絕對禁止的。

如果有兩個參數,這兩個參數可以都是對象,也可以一個是對象,一個是C ++內置類型的數據,例如:

  1. complex operator+(int a, complex &c){
  2. return complex(a+c.real, c.imag);
  3. }

它的作用是使一個整數和一個復數相加。

另外,將運算符重載函數作為全局函數時,一般都需要在類中將該函數聲明為友元函數。原因很簡單,該函數大部分情況下都需要使用類的 private 成員。

上節的最后一個例子中,我們在全局范圍內重載了+號,並在 complex 類中將運算符重載函數聲明為友元函數,因為該函數使用到了 complex 類的 m_real 和 m_imag 兩個成員變量,它們都是 private 屬性的,默認不能在類的外部訪問。

6) 箭頭運算符->、下標運算符[ ]、函數調用運算符( )、賦值運算符=只能以成員函數的形式重載。

  

運算符重載的方法步驟

全局函數、類成員函數方法實現運算符重載步驟
1)要承認操作符重載是一個函數,寫出函數名稱operator+ ()
2)根據操作數,寫出函數參數
3)根據業務,完善函數返回值(看函數是返回引用 還是指針 元素),及實現函數業務
 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Complax
 5 {
 6 private:
 7     int a;
 8     int b;
 9     // 重載友元函數 全局函數 操作符重載
10     friend Complax operator+(Complax &c1, Complax &c2);
11 public:
12     Complax(int a = 0, int b = 0)
13     {
14         this->a = a;
15         this->b = b;
16     }
17     void printC()
18     {
19         cout << "a = " << a << "\tb = " << b << endl;
20     }
21     // 2成員函數法 實現 - 運算符重載
22     Complax operator-(Complax &c2)
23     {
24 //        this->a -= c2.a;
25 //        this->b -= c2.b;
26 //        return *this;  // 這一個會改變c1的值,因為是+=
27         Complax temp(this->a - c2.a, this->b -c2.b);
28         return temp;
29 
30     }
31 
32 };
33 // 1全局函數法 實現 + 運算符重載
34 Complax operator+(Complax &c1, Complax &c2)
35 {
36     Complax temp(c1.a+c2.a, c1.b+c2.b);
37     return temp;
38 }
39 /*
40     全局函數,類成員函數方法實現運算符重載步驟
41     1:要承認運算符重載是一個函數, 寫出函數名稱
42     2: 根據操作數,寫出函數參數
43     3:根據業務,完善函數的返回值(看函數返回引用,元素,指針),及實現函數業務
44 */
45 int main()
46 {
47     Complax c1(1, 2), c2(3, 4);
48 
49     // 1全局函數法 實現 - 運算符重載
50     // Complax operator+(Complax &c1, Complax &c2)
51     Complax c3 = c1 + c2;
52     c3.printC();
53 
54     // 2成員函數法 實現 - 運算符重載
55     // Complax operator-(Complax &c2);
56     Complax c4 = c1 - c2;
57     c4.printC();
58 
59     return 0;
60 }
61 
62 運算符重載的兩種方法
++重載例子:
 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Complax
 5 {
 6 private:
 7     int a;
 8     int b;
 9     // 1全局函數法 實現 ++ 運算符重載
10     friend Complax& operator++(Complax &c1);  // 這里是返回一個引用注意,前置++
11     friend Complax operator++(Complax &c2, int); // 后置++
12 
13 public:
14     Complax(int a = 0, int b = 0)
15     {
16         this->a = a;
17         this->b = b;
18     }
19 
20     // 前置--
21     // 因為前置返回的是本身,所以返回一個引用
22     // Complax& operator--(Complax &c1)  這個是寫錯的,注意錯在哪里
23     Complax& operator--()
24     {
25         this->a --;
26         this->b --;
27         return *this;
28     }
29 
30     // 后置--  因為后置返回的是一個副本所以不用 返回引用
31     Complax operator--(int)
32     {
33         Complax tem = *this;
34         this->a--;
35         this->b--;
36         return tem;
37     }
38 
39     void printC()
40     {
41         cout << "a = " << a << "\tb = " << b << endl;
42     }
43 };
44 
45 
46 
47 // 特別注意 只有成員函數才有 this指針
48 // 1全局函數法 實現 前置++ 運算符重載
49 Complax& operator++(Complax &c1)
50 {
51     c1.a++;
52     c1.b++;
53     return c1;
54 }
55 // 后置++
56 Complax operator++(Complax &c2, int) // int防止與前置++重載而加的占位符
57 {
58     // 因為后置++是使用, 在讓c2++所以要定義一個臨時變量
59     Complax tem = c2;
60     c2.a++;
61     c2.b++;
62     return tem;
63 }
64 
65 
66 int main()
67 {
68     Complax c1(1, 2), c2(30, 40);
69 
70     // 1全局函數法 實現 前置++ 運算符重載
71     //   Complax& operator++(Complax &c1);
72     ++c1;  // 相當於 operator++(c1);
73     c1.printC();
74 
75     // 2成員函數 實現 前置-- 運算符重載
76     // 相當於c2.operator--();
77     --c2;
78     c2.printC();
79 
80     //  1全局函數法 實現 后置++ 運算符重載
81     // operator++(c2);
82     c2++;
83     //  Complax& operator++(Complax &c1);  前置++
84     //  Complax operator++(Complax &c2, int); // int防止與前置++重載而加的占位符
85     c2.printC();
86 
87     // 2成員函數 實現 后置-- 運算符重載
88     // 相當於c2.operator--();
89     // Complax operator--(int) 函數原型
90     c1--;
91     c1.printC();
92 
93     return 0;
94 }
95 
96 

 

深度拷貝:

  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdlib>
  4 using namespace std;
  5 
  6 class Name
  7 {
  8 public:
  9 
 10     Name (const char *mp)
 11     {
 12         len = strlen(mp);
 13         p = (char *)malloc(sizeof(len+1)); // 注意細節
 14         strcpy(p, mp);
 15 
 16     }
 17 
 18     //Name obj2 = obj1;
 19     // 解決方案:手工編寫拷貝構造函數,使用深拷貝
 20     Name (const Name &obj1)
 21     {
 22         len = obj1.len;
 23         p  = (char *)malloc(sizeof(len+1));
 24         strcpy(p, obj1.p);
 25     }
 26 
 27     // 成員函數 =運算符重載  不能寫全局函數
 28     Name& operator=(const Name &obj)
 29     {
 30         // 先釋放舊的內存  特別注意
 31         if(p != NULL)
 32         {
 33             delete []p;
 34             len = 0;
 35         }
 36         len = obj.len;
 37         p = new char[len+1];
 38         strcpy(p, obj.p);
 39         return *this;
 40     }
 41     ~Name ()
 42     {
 43         cout << "\t析構函數" << endl;
 44         if (p != NULL)
 45         {
 46             free(p);
 47             p = NULL;
 48             len = 0;
 49         }
 50     }
 51     void printName()
 52     {
 53         cout <<"\tp = " << this->p << "\t len = " << this->len << endl;
 54     }
 55 private:
 56     char *p;
 57     int len;
 58 };
 59 
 60 
 61 // 對象析構的時候會出現,錯誤
 62 // 調試存在bug
 63 // 析構的時候會出現二次釋放
 64 //默認的拷貝構造函數,如果要對指針進行拷貝,則只是淺拷貝,拷貝過后是兩個變量指向
 65 //同一段內存,釋放拷貝的obj2后,obj1的指針就是垃圾值,在釋放就會出錯
 66 
 67 void objmain()
 68 {
 69     Name obj1("aaaa");
 70     Name obj2 = obj1;
 71     Name obj3("sfs");
 72 
 73     //  obj3 = obj1;  // 等號操作 c++編譯器會對=進行默認的重載(同樣會發生二次釋放的問題)
 74     // 就需要手動的寫 operator= 函數
 75 
 76     cout << "成員函數=重載  ";
 77     obj3 = obj1;
 78     obj3.printName();
 79     // obj3.operator=(const Name & obj1);
 80     // void operator=(const Name &obj)  // 函數聲明
 81     // 如果要執行 obj3 = obj2 = obj1; 就需要把void 改成 Name&
 82     {
 83         obj3 = obj2 = obj1;
 84         obj3.printName();
 85         obj2.printName();
 86         obj1.printName();
 87     }
 88 
 89 }
 90 
 91 int main04()
 92 {
 93     objmain();
 94 }
 95 
 96 // 重載 = 運算符注意點
 97 // 1 先把舊的內存釋放
 98 // 2 返回一個引用 obj3 = obj1 = obj2;
 99 // 3數據進行拷貝
100 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM