一、 运算符重载
运算符重载使得用户自定义的数据以一种更简洁的方式工作。
(一)运算符重载的规则
1.重载运算符的限制
不能重载的算符
. :: .* ?: sizeof
可以重载的运算符
+ - * / % ^ & | ~
! = < > += -= *= /= %
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- ->* ‘ ->
[] () new delete new[] delete[]
a.重载运算符函数可以对运算符作出新的解释,但原有基本语义不变:
Ø不改变运算符的优先级
Ø不改变运算符的结合性
Ø不改变运算符所需要的操作数
Ø不能创建新的运算符
(二)用成员或友元函数重载运算符
Ø 运算符函数可以重载为成员函数或友元函数
1.一元运算符
Object op 或 op Object
Ø 重载为成员函数,解释为:
Object . operatorop ()
操作数由对象Object通过this指针隐含传递
Ø重载为友元函数,解释为:
operator op (Object)
操作数由参数表的参数Object提供
2. 二元运算符
ObjectL opObjectR
Ø 重载为成员函数,解释为:
ObjectL . operatorop ( ObjectR )
左操作数由ObjectL通过this指针传递,右操作数由参数ObjectR传递
Ø重载为友元函数,解释为:
operator op (ObjectL, ObjectR )
左右操作数都由参数传递
3.成员运算符函数的原型在类的内部声明格式如下:
class X {
//…
返回类型operator运算符(形参表);
//…
}
4.在类外定义成员运算符函数的格式如下:
返回类型X::operator运算符(形参表)
{
函数体
}
5.双目运算符重载为成员函数
对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。
一般而言,如果在类X中采用成员函数重载双目运算符@,成员运算符函数operator@ 所需的一个操作数由对象aa通过this指针隐含地传递,它的另一个操作数bb在参数表中显示,aa和bb是类X的两个对象,则以下两种函数调用方法是等价的:
aa @ bb; // 隐式调用
aa.operator @(bb); // 显式调用
6. 单目运算符重载为成员函数
对单目运算符而言,成员运算符函数的参数表中没有参数,此时当前对象作为运算符的一个操作数。
【例】
有一个Time类,包含数据成员minute(分)和sec(秒),模拟秒表,每次走一秒,满60秒进一分钟,此时秒又从0开始算。要求输出分和秒的值。
class Time
{
public:
Time( ){minute=0;sec=0;}
Time(int m,int s):minute(m),sec(s){ }
Time operator++( ); //声明前置自增运算符“++”重载函数
Time operator++(int); //声明后置自增运算符“++”重载函数
private:
int minute;
int sec;
};
Time Time∷operator++( ) //定义前置自增运算符“++”重载函数
{
if(++sec>=60) {
sec-=60; //满60秒进1分钟
++minute;
}
return *this; //返回当前对象值
}
Time Time∷operator++(int) //定义后置自增运算符“++”重载函数
{
Time temp(*this);
sec++;
if(sec>=60) {
sec-=60;
++minute;
}
return temp; //返回的是自加前的对象
}
一般而言,采用成员函数重载单目运算符时,以下两种方法是等价的:
@aa; // 隐式调用
aa.operator@(); // 显式调用
成员运算符函数operator@所需的一个操作数由对象aa通过this指针隐含地传递。因此,在它的参数表中没有参数。
7.用友元函数重载
友元函数重载运算符常用于运算符的左右操作数类型不同的情况。
【例】
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 ;
…...
}
Ø 在第一个参数需要隐式转换的情形下,使用友元函数重载
运算符是正确的选择
Ø 友元函数没有 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 ; }
8.成员运算符函数与友元运算符函数的比较
(1) 成员运算符函数比友元运算符函数少带一个参数(后置的++、--需要增加一个形参)。
(2) 双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但当操作数类型不相同时,必须使用友元函数。
(三)几个典型运算符重载
1.数学类中常用的几个运算符重载的特点和应用:
设 A Aobject ;
运算符 ++和 - - 有两种方式:
前置方式: ++Aobject --Aobject
成员函数 重载 A :: A operator++ () ;
解释为: Aobject . operator ++( ) ;
友元函数 重载 friend A operator++ (A &) ;
解释为: operator ++( Aobject ) ;
后置方式: Aobject ++ Aobject –
成员函数 重载 A :: A operator++ (int) ;
解释为: Aobject . operator ++( 0 ) ;——伪参数
友元函数 重载: friend A operator++ (A &, int) ;
解释为: operator++(Aobject, 0)
【例】成员函数重载++
#include<iostream>
using namespace std;
class Increase
{ public :
Increase ( ) { value=0; }
void display( ) const { cout<<value<<'\n'; } ;
Increase operator ++ ( ) ; // 前置
Increase operator ++ ( int ) ; // 后置
private: unsigned value ;
};
Increase Increase :: operator ++ ( )
{ value ++ ; return *this ; }
Increase Increase :: operator ++ ( int )
{ Increase temp; temp.value = value ++ ; return temp; }
int main( )
{ Increase a , b , n ; int i ;
for ( i = 0 ; i < 10 ; i ++ ) a = n ++ ;
cout <<"n= " ; n.display( ) ; cout <<"a= " ; a.display( ) ;
for ( i = 0 ; i < 10 ; i ++ ) b = ++ n ;
cout << "n= " ; n.display( ) ; cout << "b= " ; b.display( ) ;
}
#include<iostream>
using namespace std;
class Increase
{ public :
Increase ( ) { value=0; }
void display( ) const { cout<<value<<'\n'; } ;
friend Increase operator ++ ( Increase & ) ; // 前置
friend Increase operator ++ ( Increase &, int ) ; // 后置
private: unsigned value ;
};
Increase operator ++ ( Increase & a )
{ a.value ++ ; return a ; }
Increase operator ++ ( Increase & a, int )
{ Increase temp(a); a.value ++ ; return temp; }
int main( )
{ Increase a , b , n ; int i ;
for ( i = 0 ; i < 10 ; i ++ ) a = n ++ ;
cout <<"n= " ; n.display( ) ; cout <<"a= " ; a.display( ) ;
for ( i = 0 ; i < 10 ; i