实际上我们在学习这一部分内容时,也一直在运用运算符重载技术,例如,对+运算符,可将两个整型数相加,如1+9,也可将两个实型数相加,如2.5+9.6。这时编译系统在内部重载了两种不同数据类型的加法运算,这种重载的实质是函数重载,即int add(int,int)和float add(float,float)。但是编译系统内定的运算符重载形式,只能对系统定义的基本数据类型起作用,对用户自己定义的数据类型一无法提供相应的运算符重载形式。但我们也可以通过一些操作实现运算符重载。
1. 不可以重载的运算符
.* :: ?: sizeof
可以重载的运算符
+ - + - * / % ^ & | ~
! = < > += -= *= /= %
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- ->* ‘ ->
[] () new delete new[] delete[]
2.重载运算符的限制
不改变运算符的优先级
不改变运算符的结合性
不改变运算符所需要的操作数
不能创建新的运算符(运算符重载时一定要注意)
3.运算符重载的语法形式
运算符函数定义的一般格式如下:
<返回类型说明符> operator <运算符符号>(<参数表>)
{...
<函数体>
}
1. 用成员函数重载运算符:
成员运算符函数的原型在类的内部声明格式如下:
class X {
//…
返回类型 operator运算符(形参表);
//…
}
在类外定义成员运算符函数的格式如下:
返回类型 X::operator运算符(形参表)
{
函数体
}
(1)运算符成员函数只能定义运算符的含义,不能改变运算符的优先级和结合顺序.例如,不论按何种方式重载运算符,a+b*c始终是先乘后加;而a=b=c也要先做b=c,然后,再对a赋值.
(2)运算符重载时,不能改变其目数.例如,任何企图把%定义为单目运算符,或把!定义为双目运算符的做法都会出错.
(3)运算符函数即可在类中定义,也可以在类外定义.在类外定义时,它至少应有一个相应类的参数.
(4)无论是在类中定义的运算符成员函数,还是在类外定义的运算符函数,都可以进行重载。也就是说可以定义多个同名的运算符函数。但其参数类型应有差别,否则会产生二义性。
特别的:对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。
下面来看一个双目运算符重载为成员函数的例子
#include
class Complex
{private:
double real;
double imag;
public:
Complex( ) {real=0,imag=0;}
Complex(double r,double i) {real=r; imag=i;}
Complex operator + (Complex &c2);
void display( );
Complex Complex:: operator + (Complex &c2) {
return Complex(real+c2.real, imag+c2.imag);}
void Complex::display( ){
cout<<"("<
对于双目运算符@,当它用作成员函数时,只带一个参数(另一个运算符有调用它的对象给出),此时aa@bb与aa.operator@(bb)等价,当他用作外部函数时,带有两个参数(分别说明两个运算数),此时aa @bb与aa.operator@(aa,bb)等价。如果在类X中采用成员函数重载双目运算符@,成员运算符函数operator@ 所需的一个操作数由对象aa通过this指针隐含地传递,它的另一个操作数bb在参数表中显示,aa和bb是类X的两个对象,则以下两种函数调用方法是等价的:
aa @ bb; // 隐式调用
aa.operator @(bb); // 显式调用
对单目运算符而言,成员运算符函数的参数表中没有参数,此时当前对象作为运算符的一个操作数。
对于单目前缀运算符@,当它用作成员函数时,不必带参数(运算符就是调用它的对象),此时@aa与aa.operator@()等价,当它用作外部函数时,带有一个参数(分别说明两个运算数),此时@aa与operator@(aa)等价。
1. 用友元函数重载
运算符重载函数是友元函数
语法形式:
Typeoperator@(参数表)
{
//相对于该类而定义的操作
}
(1)不能用友元函数重载的运算符:
= 、 ( )、[]、- >
(2)注意:
对THIS所指向的数据的任何改变都会影响到激活运算数函数的对象。
可使用引用参数(指针)来解决重载时的二义性。
(3)在第一个参数需要隐式转换的情形下,使用友元函数重载运算符是正确的选择。
(4)友元函数没有this 指针,所需操作数都必须在参数表显式。
用友元函数重载运算符来实现复数的运算:
#include
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 ;
};
成员运算符函数与友元运算符函数的区别:
(1) 成员运算符函数比友元运算符函数少带一个参数(后置的++、--需要增加一个形参)。
(2) 双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但当操作数类型不相同时,必须使用友元函数。
三.几个典型的运算符重载
一、关于++和- -
1、++和- -的两种形式:
前缀:++i;后缀:i--;
2、 (1)对于前缀方式++i:
成员函数:XX::operator ++ ();
友元函数:X operator ++ (X&);
(2)对于后缀方式i++:
成员函数:XX::operator ++ (int);
友元函数:X operator ++(X&, int);
第二个参数INT一般设置为0,如:
i++ 等价于:i++(0) 或:i++=0
实例:
(1)成员函数重载++
Class X
{
…
X operator ++()
{
a++;
b++;
return *this;
}
X operator ++(int x)
{
X temp;
a++;
b++;
return temp;
}
友元函数重载++:
Class X
{
…
friend operator ++(Y &a)
{
a.x++;
b.y++;
return a;
}
friend operator ++(Y &a, int i)
{
Y temp=a;
a.x++;
b.y++;
return temp;
}
重载赋值运算符:
赋值运算符重载用于对象数据的复制
operator= 必须重载为成员函数
重载函数原型为:
类名 & 类名 :: operator= ( 类名 ) ;
重载运算符()和[]:
一、重载函数调用运算符()
1、函数为:operator();
2、格式:
X x;
x(arg1, arg2) 可被解释为:x. operator ()(arg1,arg2);
二、重载下标运算符[];
1、函数为:operator[];
2、格式:
X x;
x(y) 可被解释为:x.operator [ ](y);
3、两都者只能采用成员函数重载,不能使用友元函数。