C++笔记 ——在模板类中重载操作符

实现了一个Matrix模板类,以此为例记录一下在模板类中重载常用的运算符。

重载操作符需要特别注意的一点是函数的参数表和返回值的形式。

重载操作符有两种方法,一种是重载为成员函数,一种是重载为友元。

先实现一个矩阵类模板的框架

 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     Matirx<T>& operator++(){
 2         for(int i = 0; i < row * col; i++)
 3             values[i] ++;
 4         return *this
 5     }
 6 
 7     Matirx<T>& operator++(int a){
 8         for(int i = 0; i < row * col; i++)
 9             values[i] ++;
10         return *this;
11     }

以上两者的唯一区别是后者有参数而前者没有。实际上,这个参数表是任意的,该参数表的唯一作用是区分两者。前者用来执行前置的自加运算,比如++A,后者用来执行后置的自加运算,比如A++。某些编译器在没有重载前置自加运算符时无法重载后置自加运算符,因此一般来说这两个函数是同时被重载。

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, const 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.其他

除了面提到的运算符,常用的运算符还有复合运算符(比如+=,*=)和方括号运算符[](用于支持随机访问)以及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, const 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 private:
94     int row, col;
95     T * values;
96 };

 

上一篇:Python小程序扫描清理Redis中的key


下一篇:(三)Java8 Optional 类