浅谈C++运算符重载
一、友元函数重载
当一元运算符的操作数,或者二元运算符的左操作数是类的对象时,定义重载算符函数为成员函数。
注意:= () [] → 不能用友元重载!
友元函数重载运算符常用于运算符的左右操作数类型不同的情况
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 ; //ok!
z = 27 + z ; //no!!!
…...
}
#include<iostream.h>
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 ; }
友元函数使用传值参数,对 参数 的所有修改都无法传到函数体外,不会影响被调用的对象!
重载为友元函数,如果一个运算符的操作要修改类的对象的状态,应该使用引用参数。
1.若一元运算符的操作需要修改类对象状态时,应该用成员函数重载;
2.需要左值操作数的运算符(如 ++,–),若重载为友元函数时要用引用参数
3.C++不能用友元重载的运算符: = () [] →
4.如果运算符的操作数(尤其是第一个操作数)希望有隐式转换,则必须用友元函数重载
二、几个典型运算符重载
1.自增自减
运算符 ++和 - - 有两种方式:
前置方式: ++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'; } ;
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( ) ;
}
用成员函数重载自增/减
#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( ) ;
}
重载自增/减运算符 参数不写时 前增
2.赋值 =
= 必须重载为成员函数
重载函数原型为:
类名 & 类名 :: operator= ( 类名 ) ;
#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 ; // 调用重载赋值运算符函数
}
3.[]和()
只能用成员函数重载,不能用友元函数重载
1)下标运算符 []
用于访问数据对象的元素
//重载格式
类型 类 :: operator[] ( 类型 ) ;
类型:右操作数为符合原语义,用 int
即——类型 类 :: operator[] (int ) ;
#include<iostream>
using namespace std;
class vector
{ public :
vector ( int n ) { v = new int [ n ] ; size = n ; }
~ vector ( ) { delete [ ] v ; size = 0 ; }
int & operator [ ] ( int i ) { return v [ i ] ; }
//重载下标符号调用时作左值,使用引用
private :
int * v ; int size ;
};
int main ( )
{ vector a ( 5 ) ;
a [ 2 ] = 12 ; //[]作为左值
cout << a [ 2 ] << endl ;
}
2)函数调用符 ()
() 运算符用于函数调用
//重载格式
类型 类 :: operator() ( 参数表 ) ;
//用重载()运算符实现数学函数的抽象
#include <iostream>
using namespace std ;
class F
{ public :
double operator ( ) ( double x , double y ) ;
} ;
double F :: operator ( ) ( double x , double y )
{ return x * x + y * y ; }
int main ( )
{ F f ;
cout << f ( 5.2 , 2.5 ) << endl ;
}
4.流插入和流提取
使用友元重载!
ostream & operator << ( ostream & 输出的别名, 类名 & 对象名 )
{
自定义的内容
return 输出的别名 ;
}
istream & operator >> ( istream & 输入的别名, 类名 & 对象名 )
{
自定义的内容
return 输入的别名 ;
}
//例:为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 ;
};
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 ;
}
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 ;
}
三、(类)类型转换函数
数据类型转换在程序编译时或在程序运行实现
基本类型 ←→ 基本类型
基本类型 ←→ 类类型
类类型 ←→ 类类型
1)类类型转换
通过构造函数实现 基本类型 ←→ 类类型 的 类类型转换
class X
{ // ……
public :
X ( int ) ; //int 转换为类类型
X ( const char * , int = 0 ) ; //字符串转换为类类型
};
void f ( X arg ) ;
X a = X( 1 ) ; // a = 1
X b = "Jessie" ; // b = X ( "Jessie" , 0 )
a = 2 ; // a = X ( 2 ) 隐式调用构造函数 X ( int )
f ( 3 ) ; // f ( X ( 3 ) ) 参数为整型,隐式调用构造函数,实现类型转换
2)类型转换函数
带参数的构造函数不能把一个类类型转换成基本类型
形式:
X :: operator T ( )
{ ……
return T 类型的对象
}
功能:将类型 X 的对象转换为类型 T 的对象
1.T 可以是预定义类型,也可以是用户定义类型
2.函数没有参数,没有返回类型,但必须有一条 return 语句,返回 T 类型的对象
3.该函数只能为成员函数,不能为友元
class X
{ ……
public :
operator int ( ) ;
……
} ;
void f ( X a )
{ int i = int ( a ) ;
i = ( int ) a ; //调用: (转换的目标类型)对象名
i = a ;
}
小结
当一元运算符的操作数,或者二元运算符的左操作数是类的一个对象时,以成员函数重载。
当一个运算符的操作需要修改类对象状态时,应该以成员函数重载;如果以友元函数重载,则使用引用参数修改对象。 例如:自增/减
当运算符的操作数(尤其是第一个操作数)希望有隐式转换,则重载算符时必须用友元函数。
构造函数和类型转换函数可以实现类型转换(基本类型与类类型,以及类类型之间)。
//成员函数重载
返回类型 operator op(运算符)(参数表)
{
//相对于该类定义的操作;
}
成员函数重载默认运算符左数据为类数据成员
//友元函数重载
friend 返回类型 opeartor op(运算符)(参数表)
{
//相对于该类定义的操作
}
//重载有赋值含义的运算符时有在opeartor 前面加& (引用). 比如 []
对于赋值运算符和地址运算符系统提供默认默认重载版本
赋值运算符 = ,系统默认重载为对象数据成员的赋值
地址运算符 & ,系统默认重载为返回任何类对象的地址
一元运算符只对一个类对象进行操作,不论前置或者后置,都要求有且仅有一个操作数。对于成员函数数,操作对象有this指针隐含传递;对于友元函数们需要用形参表示
二元运算符都需要左,右两个操作数。成员函数左操作数由this指针隐含提供,右指针由参数表提供。友元函数左右操作数皆由参数表提供