C++筆記 —— 在模板類中重載操作符


實現了一個Matrix模板類,以此為例記錄一下在模板類中重載常用的運算符。

不是所有運算符都可以重載,不能被重載的運算符有:長度運算符sizeof,條件運算符?,成員選擇運算符.,域解析運算符::

重載操作符需要特別注意的一點是函數的參數表和返回值的形式。

重載操作符有兩種方法,一種是重載為成員函數,一種是重載為友元。

先實現一個矩陣類模板的框架

 1 template <typename T>
 2 class Matirx{
 3 public:
 4     Matirx(int R = 0, int C = 0):row(R), col(C), values(nullptr){
 5         if(row * col != 0)
 6             values = new T[row * col]
 7         for(int i = 0; i < row * col; i++)
 8             values[i] = T(); // 注意一下這里初始化
 9     }
10 
11     ~Matirx(){
12         if(values != nullptr)
13             delete []values;
14     }
15 
16 private:
17     int row, col;
18     T * values;
19 };

為方便起見,使用一維數組儲存矩陣,對於一個row * col 大小的矩陣A而言,它的第i行j列個元素表示成A[(i-1)*col+(j-1)]

接下來為該模板類重載操作符(以下函數皆為Matrix類內定義,只是為了方便說明單獨把每一個函數拎到外面)

1.重載+運算符以實現兩個矩陣相加

1     Matirx<T> operator+(const Matirx<T> & Other){
2         Matirx<T> temp(row, col);
3         for(int i = 0; i < row * col; i++)
4             temp.values[i] = values[i] + Other.values[i];
5         return temp;
6     }

該函數參數表使用的是 const Matrix<T> & 型的矩陣作為參數,如果把類型改成Matrix<T> 也完全不會出現編譯上的問題,使用引用是為了在該函數內不必生成一個與Other完全一致的臨時變量,減少開銷,節約時間。同時為了防止引用會直接修改傳入參數本身的值,加const限定。

該函數的返回值是Matrix<T>,該函數內的操作是產生一個和自身一樣的副本,修改該副本為兩矩陣相加的結果,返回該副本,而原矩陣不會被改變。

2.重載+運算符以實現矩陣+整數/浮點數/字符的操作

 1     template <typename T1>
 2     Matirx<T> operator+(T1 a){
 3         Matirx<T> temp(row, col);
 4         for(int i = 0; i < row * col; i++)
 5             values[i] += (T)a;
 6         return temp;
 7     }
 8 
 9     template <typename T1>
10     friend Matirx<T1> operator+(T1 a, const Matirx<T> & Other){
11         Matirx<T1> temp(row, col);
12         for(int i = 0; i < row * col; i++)
13             temp[i] = a + (T1)values[i];
14         return temp;
15     } 

假設A為Matrix<int>類的矩陣,且n為int類型整數,如果想要實現A+n和n+A,使得A中每一個元素都加n,前者應該實現為Matrix類的成員函數,后者應該實現為友元。因為A+n等價於A.operator+(n),是Matirx<int>類的成員函數。n+A等價於n.operator+(A),是int類的成員函數,只能重載為Matirx<T>類的友元

3.賦值運算符

1     Matirx<T>& operator=(const Matirx<T> & Other){
2         for(int i = 0; i < row * col; i++)
3             values[i] += Other.values[i];
4         return *this;
5     }

賦值運算符A=B等價於A.operator=(B),功能是改變A的值和B相同,因此無需產生一個部分,應該直接修改自身。如果賦值運算符的返回值為void,就會導致諸如A=B=C的連等寫法不可使用。(A=B=C等價於(A.operator=(B)).operator=(C),如果返回值為void,第二個賦值運算符將無法使用)

4.自加運算符(自減運算符同理)

 1 Matrix<T>& operator++(){
 2     for(int i = 0; i < col * row; i++)
 3         values[i] ++;
 4     return *this;
 5 }
 6 
 7 Matrix<T> operator++(int a){
 8     Matrix<T> temp = *this;
 9     for(int i = 0; i < col * row; i++)
10         values[i] ++;
11     return temp;
12 }

以上兩者的區別:

1.后者有參數而前者沒有。實際上,這個參數表是任意的,該參數表的唯一作用是區分兩者。前者用來執行前置的自加運算,比如++A,后者用來執行后置的自加運算,比如A++。

2.后者會生成一個零時對象,返回值是該零時對象,因此 i++ 的開銷要比 ++i 大,對於兩者都可以的情況(比如for循環中),應該盡量使用后者。

某些編譯器在沒有重載前置自加運算符時無法重載后置自加運算符,因此一般來說這兩個函數是同時被重載。

5.類型轉換運算符

1     template <typename T1>
2     operator Matirx<T1>(){
3         Matirx<T1> temp(row, col);
4         for(int i = 0; i < row * col; i++)
5             temp.values[i] = (T1)values[i];
6         return temp;
7     }

類型轉換運算符的重載既沒有參數表,也不需寫返回值,因為重載函數名Matirx<T>就是返回值的類型。對於一個Matirx<int>類型的矩陣A而言,在重載了類型轉換運算符后,可以使用

B=(Matrix<char>) A的方法進行類型轉換。類型轉換運算符最好在類內定義,類外定義會存在某些問題(主要是由於雙重模板引起的作用域問題)。

6.流插入和流提取運算符

 1     template <typename T1>
 2     friend ostream & operator<<(ostream & O, const Matirx<T1> & Other){
 3         for(int i = 0; i < Other.row; i++){
 4             for(int i = 0; i < Other.col; i++)
 5                 cout << Other.values[i * Other.col + j] << "  ";
 6             cout << endl;
 7         }
 8         return O;
 9     }
10 
11     template<typename T1>
12     friend istream & operator>>(istream & I, Matirx<T1> & Other){
13         for(int i = 0; i < Other.row * Other.col; i++)
14             cin >> Other.values[i];
15         return I;
16     }

這兩個運算符其實可以說的東西比較多。首先,這兩個運算符的返回值其實都可以攜程void,但是這樣寫的后果是類似cout << a << b這樣的連續輸出就不能用了,原因是,cout是在ostream類里的實例,cout << a << b等價於(cout.opeator<<(a)).operator<<(b)。其次,這兩個函數的第一個參數一定要寫引用,因為ostream和istream類是無法用戶自己實例化的。最后由於這兩個函數是友元,不能使用模板類的參數T,只能自己定義成模板使用另外一個表示符號T1。

7.比較運算符

1     template<typename T1>
2     bool operator<(const Matirx<T1> & Other){
3         if((row < Other.row) || (row == Other.row && col < Other.col))
4             return true;
5         else
6             return false;
7     }

對於比較運算符(<,>,!=,==),返回值是bool類型,比較判斷依據可自己定義。

注意:對於STL中的算法函數,a==b被定義為a<b和b<a同時不成立,而不會調用類的==運算符。

8.圓括號運算符(函數對象)

1     T operator()(int a, int b){
2         return values[(a - 1) * col + (b - 1)];
3     }

重載圓括號運算符可以讓類的實例以函數的方式被使用,重載了圓括號操作符的類又稱為函數對象類

在本例圓括號運算符被重載為獲取矩陣中某個元素的功能。

9.方括號運算符

方括號運算符有且只能有一個參數。而且在某些情況下必須提供兩個[]運算符,兩者只有聲明中const的區別。為了方便此處實現的[]運算符僅是取原矩陣中的某一行作為新矩陣的功能。

 1     Matirx<T> operator[] (int r){
 2         Matirx<T> tmp(1, col);
 3         for(int i = 0; i < col; ++i)
 4             tmp.values[i] = values[(r - 1) * col + i];
 5         return tmp;
 6     }
 7 
 8     const Matirx<T> operator[] (int r) const {
 9         Matirx<T> tmp(1, col);
10         for(int i = 0; i < col; ++i)
11             tmp.values[i] = values[(r - 1) * col + i];
12         return tmp;
13     }

10.其他

除了面提到的運算符,常用的運算符還有復合運算符(比如+=,*=)和方括號運算符[](用於支持隨機訪問)以及delete和delete[] 運算符,由於這些運算符重載方式都大同小異,基本上能在以上的幾種中找到差不多的例子,不再贅述。

完整代碼如下:

  1 template <typename T>
  2 class Matirx{
  3 public:
  4     Matirx(int R = 0, int C = 0):row(R), col(C), values(nullptr){
  5         if(row * col != 0)
  6             values = new T[row * col];
  7         for(int i = 0; i < row * col; i++)
  8             values[i] = T(); // 注意一下這里初始化
  9     }
 10     ~Matirx(){
 11         if(values != nullptr)
 12             delete []values;
 13     }
 14 
 15     Matirx<T> operator+(const Matirx<T> & Other){
 16         Matirx<T> temp(row, col);
 17         for(int i = 0; i < row * col; i++)
 18             temp.values[i] = values[i] + Other.values[i];
 19         return temp;
 20     }
 21 
 22     template <typename T1>
 23     Matirx<T> operator+(T1 a){
 24         Matirx<T> temp(row, col);
 25         for(int i = 0; i < row * col; i++)
 26             values[i] += (T)a;
 27         return temp;
 28     }
 29 
 30     template <typename T1>
 31     friend Matirx<T1> operator+(T1 a, const Matirx<T> & Other){
 32         Matirx<T1> temp(Other.row, Other.col);
 33         for(int i = 0; i < Other.row * Other.col; i++)
 34             temp[i] = a + (T1)Other.values[i];
 35         return temp;
 36     } 
 37 
 38     Matirx<T>& operator=(const Matirx<T> & Other){
 39         for(int i = 0; i < row * col; i++)
 40             values[i] += Other.values[i];
 41         return *this;
 42     }
 43 
 44     Matirx<T>& operator++(){
 45         for(int i = 0; i < row * col; i++)
 46             values[i] ++;
 47         return *this;
 48     }
 49 
 50     Matirx<T>& operator++(int a){
51 for(int i = 0; i < row * col; i++) 52 values[i] ++; 53 return *this; 54 } 55 56 template <typename T1> 57 operator Matirx<T1>(){ 58 Matirx<T1> temp(row, col); 59 for(int i = 0; i < row * col; i++) 60 temp.values[i] = (T1)values[i]; 61 return temp; 62 } 63 64 template <typename T1> 65 friend ostream & operator<<(ostream & O, const Matirx<T1> & Other){ 66 for(int i = 0; i < Other.row; i++){ 67 for(int j = 0; j < Other.col; j++) 68 cout << Other.values[i * Other.col + j] << " "; 69 cout << endl; 70 } 71 return O; 72 } 73 74 template<typename T1> 75 friend istream & operator>>(istream & I, Matirx<T1> & Other){ 76 for(int i = 0; i < Other.row * Other.col; i++) 77 cin >> Other.values[i]; 78 return I; 79 } 80 81 template<typename T1> 82 bool operator<(const Matirx<T1> & Other){ 83 if((row < Other.row) || (row == Other.row && col < Other.col)) 84 return true; 85 else 86 return false; 87 } 88 89 T operator()(int a, int b){ 90 return values[(a - 1) * col + (b - 1)]; 91 } 92 93 Matirx<T> operator[] (int r){ 94 Matirx<T> tmp(1, col); 95 for(int i = 0; i < col; ++i) 96 tmp.values[i] = values[(r - 1) * col + i]; 97 return tmp; 98 } 99 100 const Matirx<T> operator[] (int r) const { 101 Matirx<T> tmp(1, col); 102 for(int i = 0; i < col; ++i) 103 tmp.values[i] = values[(r - 1) * col + i]; 104 return tmp; 105 } 106 107 private: 108 int row, col; 109 T * values; 110 };

 


免責聲明!

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



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