[cpp] 重载运算符规律总结

重载运算符是对正常函数的语法美化.没给语言增加任何基本的东西,但改善了可理解性并降低维护费用.当用户需要时,就应该使用运算符重载,但应该仅仅以用户熟悉的语义方式来使用它。

1. 重载规则   

不能重载的运算符: . 和 .* 和 ?: 和 ::  和 sizeof 和 typeid

重载运算符有两种基本选择: 类的成员函数或者友元函数, 建议规则如下:

运算符

建议使用

所有一元运算符

成员函数

= () [] ->

必须是成员函数

+= -= /= *= ^= &= != %= >>= <<= , 似乎带等号的都在这里了.

成员函数

所有其它二元运算符, 例如: –,+,*,/

友元函数

2. 参数和返回值

     当参数不会被改变,一般按const引用来传递(若是使用成员函数重载,函数也为const).

     对于返回数值的决定:

     1) 如果返回值可能出现在=号左边, 则只能作为左值, 返回非const引用。

     2) 如果返回值只能出现在=号右边, 则只需作为右值, 返回const型引用或者const型值。

     3) 如果返回值既可能出现在=号左边或者右边, 则其返回值须作为左值, 返回非const引用。

3. 几个例子

  3.1  二元运算法建议使用友元函数重载,与内部类型的操作相似

   1:  class Integer {
   2:      int _val;
   3:  public:
   4:      Integer(int val = 0) : _val(val) {}
   5:      
   6:      // 二元运算符重载为成员函数
   7:      const Integer operator+(const Integer& i) const {
   8:              return Integer(_val + i._val);
   9:      }
  10:      // 二元运算符重载为友元函数 
  11:      friend const Integer operator-(const Integer& ,const Integer&);
  12:  };
  13:  const Integer operator-(const Integer& l,const Integer& r) {
  14:      return Integer(l._val-r._val);
  15:  }
  16:   
  17:  Integer a(40), b(12);
  18:   
  19:  a+b;       
  20:  a+1;    //  正确, 左操作数为Integer对象, +按照Integer的成员函数解析右操作数为1, 进行自动类型转换
  21:  1+a;    //  错误, 左操作数为1, +按照常规操作符解析, 而右操作符为Integer对象,1无法进行转化 
  22:  a-b;    //  正确       
  23:  a-1;    //  正确
  24:  1-a;    //  正确, 左操作数为1, 而右操作符为Integer对象,-作为Integer的一个友元函数,1被转化为Integer对象

1) 在上面的代码中, 二元操作符+作为成员函数重载,在进行形如1+a之类的调用无法正确解析。二元操作符-作为友元函数重载,使用方法与常规的-号操作符的操作类似。

   2) 如上的运算符重载, 运算符的运算结果必定位于=号的右边, 返回的是一个右值, 返回const型值。

3.2 ++与--运算符重载

1) operator++有两种形式, 后缀形式i++, 它重载时有一类型为int的虚参。前缀形式++i,它重载时没有虚参.operator--情况类似。

2) 后缀形式(i++,i--)返回原状态的拷贝(或者返回void), 前缀形式(++i, --i)通过引用返回*this。这是为了和内部类型保持一致.

    前缀版本(++i,--i)返回的是一个左值, 也就是返回一个非const引用.

    后缀版本(i++,i--)返回的是一个右值, 也就是返回一个const型值

   1:  class Example{
   2:  public:
   3:  Example(int i,int j) { _x = i; _y = j;}
   4:  // 前缀形式(++i)重载的时候没有虚参,通过引用返回*this,也就是返回变化之后的数值
   5:  const Example& Example::operator++ () {
   6:            ++_x;
   7:            ++_y;
   8:            return *this;
   9:  }
  10:  // 后缀形式(i++)重载的时候有一个int类型的虚参, 返回原状态的拷贝
  11:  const Example Example::operator++ (int) {
  12:              Example tmp(*this);
  13:              ++_x;
  14:              ++_y;
  15:              return tmp;    
  16:  }
  17:  int _x, _y;        
  18:  };
  19:   
 

3.3 重载下标运算符[]

因为下标运算符可以出现在=的左边或右边,因此它的返回值必须是个左值,一般是把返回值指定为一个引用。而为了能访问const对象,下标运算符重载有非const和const两个版本。
   1:  class Array {
   2:  public:
   3:      Array(int size) : _size(size) { _val = new int[_size];}
   4:      int& operator[] (int index) { return _val[index]; }
   5:      const int& operator[] (int index) const { return _val[index]; }
   6:  private:    
   7:      int _size;
   8:      int* _val;
   9:  };
  10:  Array array_a(10);
  11:  array_a[0] = 1;  // []的重载版本有非const版本

3.4 重载输入输出操作符

由于>>与<<操作符总是要被赋值,必须返回一个左值。

   1:  class A
   2:  {
   3:  private:
   4:      int a,b;
   5:  public:
   6:      A(int na = 0, int nb = 0):a(na), b(nb){}
   7:      friend istream& operator>>(istream& is, A& aa);
   8:      friend ostream& operator<<(ostream& os, const A& aa);
   9:   
  10:  };
  11:  ostream& operator<<(ostream& os, const A& aa) {
  12:      os << aa.a << " " << aa.b;
  13:      return os;
  14:  }
  15:   
  16:  istream& operator>>(istream& is, A& aa) {
  17:      is >> aa.a >> aa.b;
  18:      return is;
  19:  }
posted @ 2011-03-27 17:35  zsounder  阅读(3083)  评论(0编辑  收藏  举报