C++程序设计基础之(第七章)运算符重载

  • C++预定义数据类型提供了丰富的运算符集,以简捷,明确的方式操作数据。程序员也可以重载运算符函数,将运算符用于操作自定义的数据类型。
  • 本章讨论运算符重载的语法和使用,还讨论与运算符函数较为相似的类型转换函数。
    例如:
int  x , y ;
y = x + y ;
complex  c1 , c2 ;      // 复数类对象
c1 = Cadd (c1 , c2 ) ;	// 调用函数计算两个复数的和
matrix  m1 , m2 ;		// 矩阵类对象	
m1 = Madd ( m1 , m2 ) ;	// 调用函数计算两个矩阵的和

7.1 运算符重载规则

7.1.1 重载运算符的限制

在这里插入图片描述
在这里插入图片描述
重载运算符函数可以对运算符作出新的解释,但原有基本语义不变:

  • 不改变运算符的优先级
  • 不改变运算符的结合性
  • 不改变运算符所需要的操作数
  • 不能创建新的运算符

7.1.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.operator++();//调用重载版本
         ++ Counter;//调用重载版本
         cout << "\n Counter = " << Counter() ;
      }
   for( i = 0 ; i <= 5 ; i ++ )
      {  
         //Counter.operator--();调用重载版本
         -- Counter;
         cout << "\n Counter = " << Counter() ;
      }
} 
void Calculator::operator ++ ()
{ if ( value < 65535 ) value ++ ;//使用系统预定义版本
  else                           //溢出处理
    { cout << "\nData overflow !" << endl ;
      exit( 0 ) ; //abort()                //退出
    }
} 
void Calculator::operator --()
{ if ( value > 0 ) value -- ;//使用系统预定义版本
  else
    { cout << "\n Data overflow !" << endl ;
      exit( 0 ) ;
    }
}
unsigned int Calculator::operator() ()
{ return value ; }
 

结果为:
在这里插入图片描述

7.2 用成员或友元函数重载运算符

  • 运算符函数可以重载为成员函数或友元函数
  • 关键区别在于成员函数具有 this 指针,友元函数没有this指针
  • 不管是成员函数还是友元函数重载,运算符的使用方法相同。
  • 但传递参数的方式不同,实现代码不同,应用场合也不同
  1. 一元运算符

Object op 或 op Object

在这里插入图片描述
2. 二元运算符

ObjectL op ObjectR

在这里插入图片描述

7.2.1 用成员函数重载运算符

  • 当一元运算符的操作数,或者二元运算符的左操作数是类的对象时,定义重载算符函数为成员函数。
    例:建立一个描述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 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() ;
} 

程序中,重载运算符“+”的成员函数参数表只有一个参数,另一个操作数由this指针隐含传送。语句:
temp.x=x+t.x;
相当于:temp.x=this->x+t.x;
*this是引起调用函数的对象,它是运算符的左操作数。例如,表达式:
a+b
激活函数的是对象a,运算符右边的对象被作为参数传递给函数。因此,该表达式解释为:a.operator+(b)
重载运算符函数像其他函数一样,可以返回其他C++合法类型。在该程序中,重载“+”运算符函数返回类类型TriCoor,重载“++”和“=”运算符函数返回类类型的引用TriCoor&。“++”和“=”的运算可以作为左值表达式,函数返回类引用既符合运算符原来的语义,又减少了函数返回时对匿名对象数据复制的开销。
重载运算符函数中的语句:
return *this;
返回调用函数的对象,复杂表达式:
a+b+c 和 c=b=a
分别被解释为:
(a+b)+ c 和 c=(b=a)
它符合系统版本运算符的操作方式,使表达式能够正确执行。
“++”是一元运算符,由调用对象的this指针隐含传递一个参数,所以参数表为空。表达式:b++
被解释为:b.opreator++()
“++” 运算符由前置和后置的方式,它们的实现代码有区别。后面讨论。
在这里插入图片描述

7.2.2 用友元函数重载

友元函数重载运算符常用于运算符的左右操作数类型不同的情况
例如:

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.operator+(25)	
   z = 27 + z ;	//错误,被解释为 25.operator+(z) 整数值25不是Complex类对象,无法驱动函数进行运算	...
}

我们通过这个简单的例子说明了成员函数重载的“ + ”运算符不支持交换律。
在这里插入图片描述
例:复数运算

 #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 ; } //构造形参对象把整型常数25转换成复数对象(25,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() ;
}

在这里插入图片描述

使用友元重载运算符的讨论

用友元函数重载像“++”这样的运算符时,有时会碰到问题。
例如,类 TriCoor 用成员函数重载“++”的版本是:

TriCoor TriCoor :: operator ++ () 
 { x ++ ;  y ++ ;  z ++ ;  return *this ; }   // ok , 修改了this 指针所指对象

用成员函数重载一元运算符时,所需要的唯一变元通过 this 指针传递,
对 this 所指对象数据的任何改变都会影响到激活运算符函数的对象。

  1. 若定义友元函数 friend operator ++( ) 版本:
 TriCoor operator ++ (TriCoor opl )	
{ opl . x ++ ;   opl . y ++ ;    opl . z ++ ;    return   opl ; }

问题
函数使用传值参数,对 opl 的所有修改都无法传到函数体外,不会影响被调用的对象

  1. 用指向激活对象的指针定义友元函数:
 TriCoor operator ++ ( TriCoor * opl )	
{ opl -> x ++ ;   opl -> y ++ ;    opl -> z ++ ;    return   *opl ; }

问题
C++ 不知道如何激活该函数,下述代码无法编译:

 TriCoor  ob ( 1 , 2 , 3 ) ;
&ob ++ ;	// error 二义性对 ob 的地址进行递加? 还是将对象 ob 递加?
  1. 使用引用参数:
 TriCoor operator ++ ( TriCoor & opl )	
    { opl . x ++;   opl . y ++;    opl . z ++;    return   opl ; }

下述代码是正确的:

 TriCoor ob ( 1 , 2 , 3 ) ;
 ob ++;		// ok,传名

如果一个运算符的操作要修改类的对象的状态,要重载为友元函数时,应该使用引用参数。

  • 若一运算符的操作需要修改类对象状态时,应该用成员函数重载;需要左值操作数的运算符(如 ++,–),若重载为友元函数时要用引用参数
  • C++不能用友元重载的运算符: = () [] ->
  • 如果运算符的操作数(尤其是第一个操作数)希望有隐式转换,则必须用友元函数重载

7.3 几个典型运算符重载

数学类中常用的几个运算符重载的特点和应用

7.3.1 重载 ++ 与 –

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
例:成员函数重载++

#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 ++ )  b = ++ n ;
     cout << "n= " ;   n.display( ) ;   cout << "b= " ;   b.display( ) ;
 }

7.3.2 重载赋值运算符

在这里插入图片描述
例:定义Name类的重载赋值函数

#include<iostream>
#include<cstring>
using namespace std;
class  Name
{ public :
     Name ( char  *pN ) ;
     Name( const Name & ) ;		            //复制构造函数 
     Name& operator=( const Name& ) ;	    // 重载赋值运算符
     ~ Name() ;
  protected : 
     char  *pName ;
     int size ;
} ;
Name::Name ( char  *pN )
 { cout <<" Constructing " << pN << endl ;
    pName = new char[ strlen( pN ) + 1 ] ;
    if( pName != 0 ) strcpy( pName,pN ) ;
    size = strlen( pN ) ;
 }
Name::Name( const Name & Obj )	 //复制构造函数
{ cout << " Copying " << Obj.pName << " into its own block\n";
   pName = new char[strlen( Obj.pName ) + 1 ] ;
   if ( pName != 0 ) strcpy( pName, Obj.pName ) ;
   size = Obj.size;
}
Name & Name::operator= ( const Name & Obj )	 // 重载赋值运算符
{ delete  []pName ;
   pName = new char[ strlen( Obj.pName ) + 1 ] ;
   if ( pName != 0 ) strcpy( pName , Obj.pName ) ;
   size = Obj.size ;
   return *this ;
}
Name::~ Name()
{ cout << " Destructing " << pName << endl ;
   delete  []pName ;
   size = 0;
}

int main()
{ Name Obj1( "ZhangSan" ) ;
   Name Obj2 = Obj1 ;		// 初始化对象时调用复制构造函数
   Name Obj3( "NoName" ) ;
   Obj3 = Obj2 = Obj1 ;		// 修改对象时调用重载赋值运算符函数
}

在这里插入图片描述

7.3.3 重载运算符[]和()

  • 运算符 [] 和 () 是二元运算符
  • [] 和 () 只能用成员函数重载,不能用友元函数重载
  1. 重载下标运算符 []
    [] 运算符用于访问数据对象的元素
    重载格式 类型 类 :: operator[] ( 类型 ) ;
    在这里插入图片描述
  2. 重载函数调用符 ()
    () 运算符用于函数调用
    重载格式 类型 类 :: operator() ( 参数表 ) ;
    在这里插入图片描述

7.3.4 重载流插入和流提取运算符

在这里插入图片描述
例:为vector类重载流插入运算符和提取运算符

#include<iostream>
#include<cstdlib>
using namespace std;
class vector
{ public :
     vector( int size =1 ) ;       ~vector() ;
     int & operator[] ( int i ) ;
     friend ostream & operator << ( ostream & output , vector & ) ;
     friend istream & operator >> ( istream & input, vector & ) ;
  private :  
     int * v ;     int len ;
};
int main()
{ int k ;
  cout << "Input the length of vector A :\n" ;     cin >> k ;
  vector A( k ) ;
  cout << "Input the elements of vector A :\n" ;     cin >> A ;
  cout << "Output the elements of vector A :\n" ;
  cout << A ;
}
vector::vector( int size ) 
{ if (size <= 0 || size > 100 )
    { cout << "The size of " << size << " is null !\n" ; exit( 0 ) ;  }
   v = new int[ size ] ;  len = size ;
}
vector :: ~vector() { delete[] v ;  len = 0 ; }
int & vector :: operator [] ( int i ) 		
{ if( i >=0 && i < len )  return v[ i ] ;
  cout << "The subscript " << i << " is outside !\n" ;  exit( 0 ) ;
}
ostream & operator << ( ostream & output, vector & ary )
{ for(int i = 0 ; i < ary.len ; i ++ )  output << ary[ i ] << "  " ;
   output << endl ;
   return output ;
}
istream & operator >> ( istream & input, vector & ary )	
{ for( int i = 0 ; i < ary.len ; i ++ )  input >> ary[ i ] ;
   return  input ;
}
 

在这里插入图片描述

7.4 类类型转换

在这里插入图片描述

7.4.1 构造函数进行类类型转换

在这里插入图片描述
例:

class  X
{ // ……
  public :
      X ( int ) ;
      X ( const  char * , int = 0 ) ;
};
void  f ( X  arg ) ;
	:
	//调用构造函数 X ( int ) 把 1 转换为类类型 X 后赋给对象 a也称 X ( 1 ) 为 X 类的类型常量
   X  a = X( 1 ) ;		// a = 1
 
  // 调用构造函数X ( const  char * , int = 0 ) 把字符串转换为类类型 X 后赋给对象 b
   X  b = "Jessie" ;		// b = X ( "Jessie" , 0 )

//隐式调用构造函数 X ( int ) 把 2 转换为类类型 X 后赋给对象
  a = 2 ;			// a = X ( 2 )
 
 //隐式调用构造函数 X ( int ) 对实参作类类型转换,然后做参数结合
   f ( 3 ) ;			// f ( X ( 3 ) )

//当找不到匹配的构造函数时转换失败
   f ( 10 , 20 ) ;		// error
 

7.4.2 类型转换函数

  • 带参数的构造函数不能把一个类类型转换成基本类型
  • 类类型转换函数是一种特殊的成员函数,提供类对象之间显式类型转换的机制
    在这里插入图片描述
    例如:
class  X
{  ……
   public :
        operator  int ( ) ;
   ……
} ;
void  f ( X  a )
{ int  i = int ( a ) ;
   i = ( int ) a ;
   i = a ;
}

使用三种类型转换规则?
No,a 是一个类对象,它们都用类型转换函数作类型转换
X :: operator int ()
除了赋值和初始化,类型转换函数还可以这样使用:

void  g ( X  a , X  b )
{ int  i =  ( a )  ?  1 + a  :  0 ;
   int  j  = ( a && b )   ?  a + b  :  i  ;
   if ( a ) { …… } ;
}

对象 a 、b 可用在整型变量出现的地方
在这里插入图片描述
在这里插入图片描述
例:有理数计算

#include<iostream>
using namespace std;
class Rational
{ public :
       Rational() ;			//构造函数
       Rational(int n , int d=1) ;		//构造函数
       Rational(double x) ;		//构造函数,double-->Rational
       operator double() ; 		//类型转换函数,Rational-->double
       friend Rational operator+(const Rational &,const Rational &);	
       friend ostream & operator<<(ostream &,const Rational &); 
  private :
      int  Numerator , Denominator ;
} ;
int gcd( int a, int b );		//求最大公约数
int main()
{ Rational a( 2, 4 );
  Rational b = 0.3;
  Rational c = a + b;	
  cout  << double(a)  <<  " + “  <<  double(b)  <<  " = "  <<  double(c)  <<  endl ; 
  cout << a << " + " << b << " = " << c << endl;
  double x = b ;
  c = x + 1 + 0.6 ; 
  cout << x << " + " << 1 << " + " << 0.6 << " = " << double(c) << endl ;
  cout<< Rational(x) << " + " << Rational(1) << " + " << Rational(0.6) << " = " << c << endl ;
} 

Rational::Rational(int n , int d)    //用分子、分母构造对象
{ int g;
  if( d==1 )	               //分母等于1
     { Numerator = n ;	 //分子
       Denominator = d ;	 //分母
     }
  else		               //分母不等于1的有理数
     { g = gcd( n,d );	 //求分子、分母的最大公约数
       Numerator = n/g;       //约分
       Denominator = d/g;
     };
} 
Rational::Rational(double x)	//用实数构造对象
{ int a, b, g;
  a = int( x*1e5 );		//分子
  b = int( 1e5 );		//分母
  g = gcd( a,b );		//求分子、分母的最大公约数
  Numerator = a/g;	//约分
  Denominator = b/g;
}
Rational::operator double() //类型转换函数,Rational-->double
{ return double( Numerator ) / double( Denominator ); 
}
//重载运算符 + 
Rational operator+( const Rational & r1, const Rational & r2 ) 
{ int n , d ;
   n = r1.Numerator * r2.Denominator
         + r1.Denominator * r2.Numerator ;
   d = r1.Denominator * r2.Denominator ;
   return  Rational( n, d ) ;
}
//重载运算符 << 
ostream & operator<<(ostream & output, const Rational & x) { output << x.Numerator;
  if( x.Denominator!=1 ) 
      output<< "/" << x.Denominator ;
  return output;
}
int gcd( int a, int b )	//求最大公约数
{ int g ;
  if( b==0 ) g = a ;
    else g = gcd( b, a%b ) ;
  return g ;
} 

在这里插入图片描述

小结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值