(根据《C++程序设计》(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明)
对运算符重载的函数有两种处理方式:(1)把运算符重载的函数作为类的成员函数;(2)运算符重载的函数不是类的成员函数,在类中把它声明为友元函数。
1 把运算符重载函数作为类的成员函数
在上一篇博客(http://www.cnblogs.com/hust-ghtao/p/3525522.html)中,运算符重载函数 operator + 就是属于这种形式,它是Complex类中的成员函数。下面对这种方式进行分析。
例1:为了便于说明问题,将重载函数的定义重写如下:
1: Complex Complex :: operator + ( Complex& c2 )
2: {
3: Complex c ;
4: c.real = real + c2.real ;
5: c.imag = imag + c2.imag ;
6: return c ;
7: }
1: class Complex
2: {
3: public:
4: ...
5: friend Complex operator + ( Complex& c1 , Complex& c2 ) ; //重载函数作为友元函数
6: private:
7: double real ;
8: double imag ;
9: };
10:
11: Complex operator + ( Complex& c1 , Complex& c2 ) //定义运算符+重载函数
12: {
13: Complex c ;
14: c.real = c1.real + c2.real ;
15: c.imag = c1.imag + c2.imag ;
16: return c ;
17: }
这个程序和把运算符重载函数作为类的成员函数相比,只做了一处改动,就是将运算符重载函数作为类外的普通函数,并在Complex类中声明它为友元函数。可以看到运算符重载函数有两个参数。C++编译系统将程序中的表达式 c1 + c2 解释为operator + ( c1 , c2 ) 。
有的读者可能会产生疑问:为什么把运算符函数作为友元函数?理由很简单,因为运算符函数要访问Complex类对象中的成员。如果运算符函数不是Complex类的友元函数,而是一个普通函数,它是没有权利访问Complex类的私有成员的。
3 将运算符重载函数作为类的成员函数和友元函数的区别
如果将运算符重在为类的成员函数,它可以通过this指针*地访问本类的数据成员,因此至少可以少写一个函数的参数。但必须要求运算表达式中第一个参数(即运算符左侧的操作数)是一个类对象,而且与运算符函数的类型相同。因为必须通过类的对象去调用该类的成员函数,而且只有运算符的返回值与该对象同类型,运算结果才有意义。
例如在例1中,将运算符+重载为类的成员函数,则表达式 c1+c2中第一个参数c1是Complex类对象,运算符函数的返回值的类型也是Complex。
当然这种规定也会带来问题,如想将一个复数和一个整数相加,如c1+i,可以运算符+作为成员函数,形式如下:
1: Complex Complex :: operator + ( int& i )
2: {
3: return Complex( real+i ,imag ) ;
4: }
则在表达式必须是 c3 = c2 + i ;而不能写成c3 = i + c2 ;
如果要求程序员时刻记得重载运算符左右的参数类型,显然不是明智的选择。另外如果出于某种考虑,要求运算符左侧的操作数属于C++的标准类型或是一个其他类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数。如果需要访问类的私有成员,则必须声明为友元函数。
如还是将一个复数和一个整数相加,如c1+i,可以运算符+作为友元函数,形式如下:
在Complex类中声明:
1: friend Complex operator + ( int& i , Complex& c ) ; //第一个参数可以不是类对象
2: friend Complex operator + ( Complex& c , int& i ) ;
在类外定义友元函数:
1: Complex operator + ( int& i , Complex& c )
2: {
3: return Complex( i+c.real , c.imag ) ;
4: }
5:
6: Complex operator + ( Complex& c , int& i )
7: {
8: return Complex( i+c.real , c.imag ) ;
9: }
经过这样的处理之后,表达式 c3 = i + c2 ;和 c3 = c2 + i ; 都是合法的。编译系统会根据表达式的形式选择调用与之匹配的运算符重载函数。当然也可以把一个运算符重载函数作为友元函数,另一个作为成员函数。但不能两个都作为成员函数,原因显然哈。
究竟把运算符重载函数作为类的成员函数好,还是友元函数好?由于友元函数的使用会破坏类的封装,因此,从原则上说,要尽量将运算符函数作为成员函数。但应统筹考虑各方面的因素,以下提供具体原则,仅供参考:
(1)C++规定,赋值运算符“=”、下标运算符“[]”、函数调用运算符“()”、成员运算符“->”必须作为成员函数。
(2)流插入运算符“<<”和刘提取运算符“>>”、类型转换运算符只能作为友元函数。
(3)一般将单目运算符和复合运算符(+=、-=、/=、*=、!=、^=、%=、>>=、<<=)重载为成员函数。
(4)一般将双目运算符重载为友元函数。