實現了一個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 };