• 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 };
  • 相关阅读:
    oracle 日期函数
    SharpDevelop学习笔记(5)—— AddIns系统详解
    C#3.0 为我们带来什么(2) —— 自动属性
    SharpDevelop学习笔记(6)—— AddIn构建指南
    SharpDevelp2.0学习笔记(1)——SharpDevelp简单介绍
    对象数组根据某属性列的灵活排序
    SharpDevelop学习笔记(4)——SharpDevelop的核心
    也谈2007
    SharpDevelop学习笔记(2)——体系结构
    C#3.0 为我们带来什么(1) —— LINQ之Lambda
  • 原文地址:https://www.cnblogs.com/LC32/p/13090627.html
Copyright © 2020-2023  润新知