导图社区 cpp系列知识框架----运算符重载
c 系列知识框架,本部分为运算符重载(使同一个运算符作用于不同类型的数据时具有不同的行为)的相关知识点,希望这份脑图会对你有所帮助。
编辑于2023-06-03 03:12:03运算符重载
定义
使同一个运算符作用于不同类型的数据时具有不同的行为。
实质上将运算对象转化为运算函数的实参,并根据实参的类型来确定重载的运算函数。
运算符重载和类型重载是多态的另外两种表现形式。
规则
只能重载C++中已有的运算符,不能臆造新的运算符;
类属关系运算符“.”、作用域分辨符“::”、成员指针运算符“*”、sizeof运算符和三目运算符“?:”不能重载。
重载之后运算符的优先级和结合性都不能改变;单目运算符只能重载为单目运算符,双目运算符只能重载为双目运算符;重载运算符的函数不能有默认的参数;
运算符重载后的功能应当与原有功能相类似。必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用)。
重载运算符含义必须清楚,不能有二义性。用于类对象的运算符一般必须重载,但运算符“=”和“&”不必用户重载。
形式
重载为类的成员函数;
声明格式
<类型> <类名>:: operator <要重载的运算符>(形参表) { 函数体 }
重载为类的友元函数。
声明格式
friend <函数类型> operator <运算符>(形参表) { 函数体; }
限制
不可臆造新的运算符。必须把重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中。
不能改变运算符操作数的个数;
不能改变运算符原有的优先级;
不能改变运算符原有的结合性;
不能改变运算符原有的语法结构;
特点
运算符重载函数名必须为:operator<运算符>
运算符的重载是通过运算符重载函数来实现的。对于二元运算符重载函数,函数的参数通常为一个即右操作数,运算符的左操作数为调用重载函数的对象。
对于一元运算符重载函数,运算符的左操作数或右操作数为调用重载函数的对象。
运算符重载函数的返回类型:若对象进行运算后的结果类型仍为原类型,则运算符重载函数的返回类型应为原类型。
重载为成员函数
双目运算
形式
oprd1 B oprd2
本质
oprd1 + oprd2就相当于函数调用 oprd1.operator +(oprd2)。
单目运算
前置单目运算
形式
U oprd
<函数类型> operator ++( );
本质
++ oprd 就相当于函数调用 oprd.operator ++( );
后置单目运算
形式
oprd V
<函数类型> operator - - ( int );
占位符
本质
oprd-- 就相当于函数调用 oprd.operator - -
如果我们把“operator+”看成函数名 这时,operator+就完全是一个函数了 c3=c1.operator+(c2);
重载为友元函数
双目运算
形式
oprd1 B oprd2
声明
friend <函数返回类型> operator <重载运算符>(<形参1>, <形参2>);
实现
<函数返回类型> operator <重载运算符>(<形参1>, <形参2>) { 函数体; }
本质
oprd1 B oprd2就相当于函数调用 operator B (oprd1,oprd2)
单目运算
实现
一元运算符重载为友元函数的一般格式为: <函数返回类型> operator<一元运算符> (类名 &对象) { 函数体 }
前置单目运算
形式
U oprd
本质
U oprd相当于函数调用 operator U(oprd)。
后置单目运算
形式
oprd V
本质
oprd V相当于函数调用 operator V(oprd,int)。
占位符
如果我们把“operator+”看成函数名 这时,operator+就完全是一个函数了 c3=c1.operator+(c2);
说明
重载函数作为友元普通函数时,重载函数不能用对象调用,所以参加运算的对象必须以形参方式传送到重载函数体内,在二元运算符重载函数为友元函数时,形参通常为二个参加运算的对象。
参数与返回值
对于任何函数参数,如果仅需要从参数中读而不改变它,缺省地应当按const引用来传递它。普通算术运算符(像+和-号等)和布尔运算符不会改变参数,所以以const引用传递是使用的主要方式。当函数是一个类成员的时候,就转换为const成员函数。只是对于会改变左侧参数的赋值运算符(像+ =)和运算符‘=’,左侧参数才不是常量( constant ),但因为参数将被改变,所以参数仍然按地址传递。
应该选择的返回值取决于运算符所期望的类型。如果运算符的效果是产生一个新值,将需要产生一个作为返回值的新对象。
所有赋值运算符改变左值。为了使得赋值结果用于链式表达式(像A = B = C),应该能够返回一个刚刚改变了的左值的引用。但这个引用应该是const还是nonconst呢?虽然我们是从左向右读表达式A = B = C,但编译器是从右向左分析这个表达式,所以并非一定要返回一个nonconst值来支持链式赋值。然而人们有时希望能够对刚刚赋值的对象进行运算,例如(A = B). foo( ),这是B赋值给A后调用foo( )。因此所有赋值运算符的返回值对于左值应该是nonconst引用。
对于逻辑运算符,人们希望至少得到一个int返回值,最好是bool返回值。(在大多数编译器支持C + +内置bool类型之前开发的库函数使用int或typedef等价物)。
当有不同类型的参数时,要注意参数的自动类型转换(称为隐式类型转换),隐式类型转换在很多地方可以简化程序的书写,但是也可能留下隐患。
注意事项
当一个重载操作符的含义不明显时,给操作取一个名字更好。对于很少用的操作,使用命名函数通常比用操作符更好。如果不是普通操作,没必要为简洁而使用操作符。
重载逗号、取地址、逻辑与、逻辑或等操作符通常不是好做法。这些操作符具有有用的内置含义,如果我们定义了自己的版本,就不能再使用这些内置含义。
如果类定义了相等操作符,也应该定义不等操作符!=。同样,如果定义了<,则可能应该定义四个关系操作符(>,>=,<,<=)。
重载流插入和流提取运算符
输出操作符<<的重载
形式
ostream & operator << (ostream &,const 自定义类 &);
说明
第一个参数和函数的类型都必须是ostream&类型
第二个参数是对要进行输出的类类型的引用,它可以是const,因为一般而言输出一个对象不应该改变对象。返回类型是一个ostream引用,通常是输出操作符所操作的ostream对象。
例子
ostream &operator<<(ostream &output,Date &d) { output<<d.year<<“-”<<d.month<<“-”<<d.day; return output; }
输入操作符>>的重载
形式
istream & operator >> (istream &, 自定义类 &);
说明
第一个形参是一个引用,指向要读的流,并且返回的也是同一个流的引用。
第二个形参是对要读入的对象的非const 引用,该形参必须为非const,因为输入操作符的目的是将数据读到这个对象中。和输出操作符不同的是输入操作符必须处理错误和文件结束的可能性。
例子
istream& operator>>(istream &input,Date &d) { input>>d.year>>d.month>>d.day; if(!input) { d=Date(); } return input; }
类型重载
定义
C++中提供了类型转换函数,可以将一种类类型对象转换成另一种类类型的对象,这就是类型重载。
形式
class <类名1> { public: operator <类名2>( ); …… }; <类名1>::operator <类名2>() { 函数体; }
说明
类中类型转换函数必须是非静态的成员函数
不能定义成友元函数
无返回值类型且不带参数
例子
#include <iostream> using namespace std; class Time { private: int hour,minute,second; public: Time(int h=0,int m=0,int s=0); void Show();//显示时:分:秒的成员函数 operator float(); }; Time::Time(int h,int m,int s) { hour=h; minute=m; second=s; } void Time::Show() { cout<<hour<<":"<<minute<<":"<<second<<endl; } Time::operator float() { float sec; sec=hour*3600+minute*60+second; //cout<<"second="<<sec<<endl; return sec; } int main() { float s1,s2,s3; Time t(10,15,20); s1=t; s2=float(t); t.Show(); s3=(float)t; cout<<"s1="<<s1<<'\t'<<"s2="<<s2<<'\t'<<"s3= "<<s3<<endl; } 结果 10:15:20 s1=36920 s2=36920 s3= 36920
转换运算符重载
隐式数据类转换
显式数据类型转换,也叫强制类型转换。
形式
operator 类型名 () ;
特点
1、没有返回值 2、功能类似强制转换
例子
class RMB { public: RMB(double value=0.0) { yuan =value; fen = (value-yuan)*100+0.5; } void ShowRMB() { cout<<yuan<< “元” <<fen<< "分" <<endl; } operator double () { return yuan+fen/100.0; } private: int yuan, fen; }; void main() { RMB r1(1.01),r2(2.20); RMB r3; //显式转换类型 r3 = RMB((double)r1+(double)r2); r3.ShowRMB(); //自动转换类型 r3=r1+2.40; r3.ShowRMB(); //自动转换类型 r3 =2.0-r1; r3.ShowRMB(); }
运算符new和delete重载
new
void *operator new ( size_t size)//size_t unsigned integer { return malloc( size ) ; }
delete
void operator delete( void *p ) { free( p ) ; }
逗号运算符重载
例子
//例15 : 演示重载逗号“,”和加号“+”运算符的程序 #include <iostream.h> #include<malloc.h> class point { private: int x, y ; public: point( ){ } ; point( int xx, int yy ) { x=xx ; y=yy ; } point operator , (point r) { return r ; } point operator + (point r) { point t ; t.x=x + r.x ; t.y=y + r.y ; return t ; } void disp( ) { cout<<"area: "<<x*y<<endl ; } } ; void main( ) { point p1(1, 2), p2(3, 4), p3(5, 6) ; p1.disp( ) ; p2.disp( ) ; p3.disp( ) ; p1=(p1, p2+p3, p3) ; //返回右操作数p3的坐标 p1.disp( ) ; } 程序运行结果为: area: 2 area: 12 area: 30 area: 30