文章目录
一、运算符重载规则
1. 重载运算符的限制
可以重载的运算符
+ - * / % ^ & | ~
! = < > += -= *= /= %
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- ->* ‘ ->
[] () new delete new[] delete[]
不能重载的算符
. :: .* ?: sizeof
重载运算符函数可以对运算符作出新的解释,但原有基本语义不变:
1.不改变运算符的优先级
2.不改变运算符的结合性
3.不改变运算符所需要的操作数
4.不能创建新的运算符
2.运算符重载的语法形式
运算符函数是一种特殊的成员函数或友元函数
成员函数的语法形式为:
类型(返回类型) 类名(重载该运算的类) :: operator op(被重载的运算符) ( 参数表 )
{
// 相对于该类定义的操作
}
一个运算符被重载后,原有意义没有失去,只是定义了相对一特定类的一个新运算符
#include<iostream>
#include<cstdlib>
using namespace std;
class Calculator
{ public:
Calculator() { value = 0 ; } ;
void operator ++ () ;
void operator -- () ;
unsigned int operator() () ;
private:
unsigned int value;
};
int main()
{ Calculator Counter ;
int i ;
for( i = 0 ; i < 5 ; i ++ )
{ ++ Counter;
cout << "\n Counter = " << Counter() ;
}
for( i = 0 ; i <= 5 ; i ++ )
{ -- Counter;
cout << "\n Counter = " << Counter() ;
}
}
void Calculator::operator ++ ()
{ if ( value < 65535 ) value ++ ;
else
{ cout << "\nData overflow !" << endl ;
exit( 0 ) ;
}
}
void Calculator::operator --()
{ if ( value > 0 ) value -- ;
else
{ cout << "\n Data overflow !" << endl ;
exit( 0 ) ;
}
}
unsigned int Calculator::operator() ()
{ return value ; }
二、用成员或友元函数重载运算符
关键区别在于成员函数具有 this 指针,友元函数没有this指针
不管是成员函数还是友元函数重载,运算符的使用方法相同。
但传递参数的方式不同,实现代码不同,应用场合也不同
1.一元运算符
Object op 或 op Object
①重载为成员函数,解释为:
Object . operator op ()
操作数由对象Object通过this指针隐含传递
操作数由对象Object通过this指针隐含传递
②重载为友元函数,解释为:
operator op (Object)
操作数由参数表的参数Object提供
操作数由参数表的参数Object提供
2.二元运算符
ObjectL op ObjectR
①重载为成员函数,解释为:
ObjectL . operator op ( ObjectR )
左操作数由ObjectL通过this指针传递,右操作数由参数ObjectR传递
②重载为友元函数,解释为:
operator op ( ObjectL, ObjectR )
左右操作数都由参数传递
3.用成员函数重载运算符
当一元运算符的操作数,或者二元运算符的左操作数是类的对象时,定义重载算符函数为成员函数
成员函数不能适用于左操作数不是本身的运算符重载
举例:建立一个描述3维坐标的类 Tri_Coor,重载运算符 “+”、“++”、和 “=” ,实现简单的算术运算
#include<iostream>
using namespace std;
class TriCoor
{ public:
TriCoor( int mx = 0, int my = 0, int mz = 0 ) { x = mx ; y = my ; z = mz ; }
TriCoor operator + ( TriCoor t )
{ TriCoor temp ;
temp.x = x+t.x ; temp.y = y+t.y ; temp.z = z+t.z ;
return temp ;
}
TriCoor operator = ( TriCoor t ) { x = t.x ; y = t.y ; z = t.z ; return &this ; }
//可以将TriCoor t 转换为const TriCoor& t 来避免拷贝构造带来的浪费
TriCoor operator ++ () { x ++ ; y ++ ; z ++ ; return &this ; }
void show() { cout << x << " , " << y << " , " << z << "\n"; }
void assign( int mx, int my, int mz ) { x = mx; y = my; z = mz; }
private: int x, y, z ; // 3_d coordinates
} ;
int main()
{ TriCoor a( 1, 2, 3 ), b, c ;
a.show(); b.show(); c.show();
for( int i = 0; i < 5; i ++ ) ++ b; b.show() ;
c.assign( 3, 3, 3 ) ; c = a + b + c ; c.show() ;
c = b = a ; c.show() ;
}
4.用友元函数重载
友元函数重载运算符常用于运算符的左右操作数类型不同的情况
例如:
class Complex
{ int Real ; int Imag ;
public :
Complex ( int a ) { Real = a ; Imag = 0 ; }
Complex ( int a , int b ) { Real = a ; Imag = b ; }
Complex operator + ( Complex ) ;
…...
} ;
int f ( )
{ Complex z ( 2 , 3 ) , k ( 3 , 4 ) ;
z = z + 27 ; // 左操作数为对象可以使用成员函数
z = 27 + z ; //左操作数为27不能使用成员函数
…...
}
①条件
在第一个参数需要隐式转换(利用的重载参数的特性)的情形下,使用友元函数重载
运算符是正确的选择友元函数没有 this 指针,所需操作数都必须在参数表显式
声明,很容易实现类型的隐式转换C++中不能用友元函数重载的运算符有
= () [] ->
②例子
#include<iostream>
using namespace std;
class Complex
{ public:
Complex( double r =0, double i =0 ) { Real = r ; Image = i ; }
Complex(int a) { Real = a ; Image = 0 ; }
//这个是将传入常数的情况确定
void print() const ;
friend Complex operator+ ( const Complex & c1, const Complex & c2 ) ;
friend Complex operator- ( const Complex & c1, const Complex & c2 ) ;
friend Complex operator- ( const Complex & c ) ;
private:
double Real, Image ;
};
Complex operator + ( const Complex & c1, const Complex & c2 )
{ double r = c1.Real + c2.Real ; double i = c1.Image+c2.Image ;
return Complex ( r, i ) ;
//创造临时变量没有命名
}
Complex operator - ( const Complex & c1, const Complex & c2 )
{ double r = c1.Real - c2.Real ; double i = c1.Image - c2.Image ;
return Complex ( r, i ) ;
}
Complex operator- ( const Complex & c )
{ return Complex ( -c.Real, - c.Image ) ; }
void Complex :: print() const
{ cout << '(' << Real << " , " << Image << ')' << endl ; }
int main()
{ Complex c1( 2.5,3.7 ), c2( 4.2, 6.5 ) ;
Complex c ;
c = c1 - c2 ; // operator-(c1,c2)
c.print() ;
c = 25 + c2 ; // operator+(25,c2)
c.print() ;
c = c2 + 25 ; // operator+(c2,52)
c.print() ;
c = - c1 ; // operator-(c1)
c.print() ;
}
③注意
-
friend的作用:
举例:
Complex operator - ( const Complex & c1, const Complex & c2 ) { double r = c1.Real - c2.Real ; double i = c1.Image - c2.Image ; return Complex ( r, i ) ; }
虽然在这个函数中,这个类的实例可以调用本身的私有数显
但是对于传入的参数const Complex & c1, const Complex & c2的私有属性,这个函数如果不是友元函数那么就不难调用c1.Real等
5.讨论
用友元函数重载像“++”这样的运算符时,有时会碰到问题。
例如,
①类 TriCoor 用成员函数重载“++”的版本是:
TriCoor TriCoor :: operator ++ ()
{ x ++ ; y ++ ; z ++ ; return *this ; } // ok , 修改了this 指针所指对象
用成员函数重载一元运算符时,所需要的唯一变元通过 this 指针传递,
对 this 所指对象数据的任何改变都会影响到激活运算符函数的对象。
②若定义友元函数 friend operator ++( ) 版本:
TriCoor operator ++ (TriCoor opl )
{ opl . x ++ ; opl . y ++ ; opl . z ++ ; return opl ; }
//函数使用传值参数,对 opl 的所有修改都无法传到函数体外,不会影响被调用的对象
③用指向激活对象的指针定义友元函数:
TriCoor operator ++ ( TriCoor * opl )
{ opl -> x ++ ; opl -> y ++ ; opl -> z ++ ; return *opl ;
TriCoor ob ( 1 , 2 , 3 ) ;
&ob ++ ; // error
二义性:对 ob 的地址进行递加?还是将对象 ob 递加?
④使用引用参数:
TriCoor operator ++ ( TriCoor & opl )
{ opl . x ++; opl . y ++; opl . z ++; return opl ; }
TriCoor ob ( 1 , 2 , 3 ) ;··
ob ++; // ok,传名
如果一个运算符的操作要修改类的对象的状态,要重载为友元函数时,应该使用引用参数。
6.总结
①若一运算符的操作需要修改类对象状态时,应该用成员函数重载;
需要左值操作数的运算符(如 ++,–),若重载为友元函数时要用引用参数
②C++不能用友元重载的运算符: = () [] ->
③如果运算符的操作数(尤其是第一个操作数)希望有隐式转换,则
必须用友元函数重载