《第四章-运算符重载优秀PPT.ppt》由会员分享,可在线阅读,更多相关《第四章-运算符重载优秀PPT.ppt(80页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、4.1什么是运算符重载什么是运算符重载C+为程序员供应了敏捷的手段,让程序员自己定义类,自己设计相应的运算符(必需在已有的运算符基础上设计),使之应用于自己定义的类。与函数重载类似,对已有的运算符赐予新的含义,用一个运算符表示不同功能的运算,这就是运算符重载。事实上,我们在此之前已经运用了运算符重载。如是C+的移位运算符,它又与流对象cout协作作为流插入运算符,这是C+对进行了重载处理。运算符重载的实质运算符重载的实质v运算符重载是对已有的运算符赐予多重含义。v必要性vC+中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类)v实现机制v将指定的运算表达式转化为对运算
2、符函数的调用,运算对象转化为运算符函数的实参。v编译系统对重载运算符的选择,遵循函数重载的选择原则。例4.1 通过成员函数实现复数的加法。class Complex private:double real;double imag;public:Complex()real=0;imag=0;Complex(double r,double i)real=r;imag=i;Complex complex_add(Complex&c2);void display();Complex Complex:complex_add(Complex&c2)Complex c;c.real=real+c2.real
3、;c.imag=imag+c2.image;return c;void Complex:display()cout(real,imagi)endl;int main()Complex c1(3,4),c2(5,-10),c3;c3=c1 plex_add(c2);coutc1=;c1.display();coutc2=;c2.display();coutreal+c2.real;c.imag=this-imag+c2.imag;在main函数中通过对象c1调用加法函数,上面的语句相当于:c.real=c1.real+c2.real;c.imag=c1.imag+c2.imag;能否用+运算符实
4、现复数加法?4.2运算符重载的方法运算符重载的方法运算符重载的方法是定义一个重载运算符函数,在须要时系统自动调用该函数,完成相应的运算。运算符重载实质上是函数的重载。运算符重载函数的格式是:数据类型 operator 运算符(形参表)重载处理 数据类型:是重载函数值的数据类型。operator 是保留字规则和限制规则和限制vC+中可以重载除下列运算符外的全部运算符:.*:?:sizeof()v只能重载C+语言中已有的运算符,不行臆造新的。v不变更原运算符的优先级和结合性。v不能变更操作数个数。v经重载的运算符,其操作数中至少应当有一个是自定义类型。两种形式两种形式v重载为类成员函数。v重载为友
5、元函数。运算符函数运算符函数v声明形式v函数类型 operator 运算符(形参)vv .vv重载为类成员函数时 参数个数=原操作数个数-1(后置+、-除外)v重载为友元函数时 参数个数=原操作数个数,且至少应当有一个自定义类型的形参。不能重载的运算符只有5个:.成员运算符.*成员指针运算符:域运算符sizeof 长度运算符?:条件运算符重载函数名是由operator和运算符联合组成。复数加法运算符重载函数原型可以是:Complex operator+(Complex&c2);例4.2 重载运算符+,用于两个复数相加。分析:定义一个复数类,用成员函数实现加号的重载函数。两个复数相加结果仍是复数
6、,所以函数的返回值的类型也是复数类。用成员函数实现运算符重载函数时,调用格式是“对象名.成员名”,此时对象就是一个参与运算的操作数,加法还须要另一个操作数,这个操作数用函数的参数传递,参数的类型就是复数类。而运算结果用函数值返回。class Complex public:Complex()real=0;imag=0;Complex(double r,double i)real=r;imag=i;Complex operator+(Complex&c2);void display();private:double real;double imag;Complex Complex:operator
7、+(Complex&c2)Complex c;c.real=real+c2.real;c.imag=imag+c2.imag;return c;void Complex:display()cout(real,imagi)endl;int main()Complex c1(3,4),c2(5,-10),c3;c3=c1+c2;coutc1=;c1.display();coutc2=;c2.display();coutc1+c2=;c3.display();return 0;说明:(1)用运算符重载函数取代了例 4.1中的加法成员函数,从外观上看函数体和函数返回值都是相同的。(2)在主函数中的表达
8、式c3=c2+c1 取代了例4.1中的c3=c1 plex_add(c2),编译系统将表达式c3=c1+c2 说明为 c1.operator+(c2)对象c1调用的重载函数operator+,以c2为实参计算两个复数之和。请考虑在例4.2中能否用一个常量和一个复数相加?如 c3=3+c2;/错误应当定义对象:Complex C1(3.0,0):c3=C1+c2;留意:运算符重载后,其原来的功能仍旧保留,编译系统依据运算表达式的上下文确定是否调用运算符重载函数。运算符重载和类结合起来,可以在C+中定义运用便利的新数据类型。4.3重载运算符的规则重载运算符的规则(1)C+只允许已有的部分运算符实施
9、重载。(2)不能重载的运算符有五个。(3)重载不变更操作数的个数。(4)重载不变更运算符的优先级。(5)运算符重载函数不能带默认值参数。(6)运算符重载函数必需与自定义类型的对象联合运用,其参数至少有一个类对象或类对象引用。(7)C+默认供应=和&运算符重载。v(8)运算符重载函数可以是类成员函数也可以是类的友元函数,还可以是一般函数。v(9)C+规定赋值运算符、下标运算符、函数调用运算符必需定义为类的成员函数;而输出流插入、输入流提取、类型转换运算符不能定义为类的成员函数。4.4运算符重载函数作为类成员函数和友运算符重载函数作为类成员函数和友元函数元函数在例4.2程序中对运算符+进行了重载,
10、该例将运算符重载函数定义为复数类的成员函数。从该程序中看到运算符重载为成员函数时,带一个类类型的形参,而另一个加数就是对象自己。例4.3 将加法运算符重载为适用于复数加法,重载函数作为类的友元函数。#include class Complex public:Complex()real=0;imag=0;Complex(double r)real=r;imag=0;Complex(double r,double i)real=r;imag=i;friend Complex operator+(Complex&c1,Complex&c2);void display();private:double
11、 real;double imag;Complex operator+(Complex&c1,Complex&c2)return Complex(c1.real+c2.real,c1.imag+c2.imag);/显式调用构造函数显式调用构造函数 void Complex:display()cout(real,imagi)endl;int main()Complex c1(3,4),c2(5,-10),c3;c3=c1+c2;coutc1=;c1.display();coutc2=;c2.display();coutc1+c2=;c3.display();return 0;加法运算符重载为友元
12、函数,C+在编译时将表达式c1+c2说明为operator+(c1,c2)即相当于执行以下函数Complex operator+(Complex&c1,Complex&c2)return Complex(c1.real+c2.real,c1.imag+c2.imag);因为一般函数是不能干脆访问对象的私有成员,假如一般函数必需访问对象的私有成员,可调用类的公有成员函数访问对象的私有成员。这会降低效率。书上的错误书上的错误如想将一个复数和一个整数相加,运算符重载函数作为成员函数定义如下:Complex Complex:operator+(int&i )return Complex(real+i,
13、imag);留意在运算符+的左侧必需是Complex类对象,程序中可以写成:c3=c2+n不能写成:c3=n+c2书上的错误书上的错误假如要求在运用重载运算符时,运算符左侧操作数不是对象,就不能运用前面定义的运算符重载函数,可以将运算符重载函数定义为友元函数:friend Complex operator+(int&i,Complex&c)return Complex(c.real+i,c.imag);友元函数不要求第一个参数必需是类类型,但是要求实参要与形参一一对应:c3=n+c2 /依次正确c3=c2+n /依次错误为了实现加法的交换率,必需定义两个运算符重载函数,记住成员函数要求运算符左
14、侧的操作数必需是自定义类型的对象,而友元函数没有这个限制,可以用下面两个组合中随意一个:(1)成员函数(左操作数是对象,右操作数是非对象)、友元函数(左操作数是非对象,右操作数是对象)(2)友元函数(左操作数是对象,右操作数是非对象)、友元函数(左操作数是非对象,右操作数是对象)由于运用友元会破坏类的封装,要尽量将运算符重载函数定义为成员函数。但考虑到各方面的因素,一般将单目运算符重载为成员函数,将双目运算符重载为友元函数。VC+6.0的不带后缀h的头文件不支持把成员函数重载为友元函数。但VC+6.0带后缀h的头文件支持这项功能,所以要将程序中的语句:#include using namesp
15、ace std;改成:#include 即可正常运行。4.5重载双目运算符重载双目运算符v双目的意思是运算符左边和右边的操作数均参与运算。v假如要重载 B 为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中 oprd1 为A 类对象,则 B 应被重载为 A 类的成员函数,形参类型应当是 oprd2 所属的类型。v经重载后,表达式 oprd1 B oprd2 相当于 oprd1.operator B(oprd2)。v例4.4 定义一个字符串类String,用来处理不定长的字符串,重载相等、大于、小于关系运算符,用于两个字符串的等于、大于、小于的比较运算。v操作数:两个操作数都是字
16、符串类的对象。v规则:两个字符串进行比较。v将“”运算重载为字符串类的成员函数。(1)先建立一个String类#include#include class String /String 是用户自己指定的类名是用户自己指定的类名 public:String()p=NULL;String(char*str);void display();private:char*p;String:String(char*str)p=str;void String:display()coutp;int main()String string1(Hello),string2(Book);string1.display
17、();coutendl;string2.display();return 0;先编写出简洁的程序框架,编写和调试都比较便利。构造函数是把定义对象时的实参的地址赐予数据成员p,p是指向实参的指针。程序实现了建立对象、输出字符串对象的功能。程序运行结果分别输出HelloBook(2)有了这个基础后,再增加所需的其他内容,先重载大于运算符。程序如下:/本程序适用于VC+6.0#include#include class String public:String()p=NULL;String(char*str);void display();friend bool operator(String&st
18、ring1,String&string2);private:char*p;String:String(char*str)p=str;void String:display()cout(String&string1,String&string2)if(strcmp(string1.p,string2.p)0)return true;else return false;int main()String string1(Hello),string2(Book);coutstring2)endl;return 0;运算符重载函数定义为友元函数,函数值是布尔类型,在函数中调用了strcmp库函数,str
19、ing1.p指向“Hello”,string2.p指向“Book”,程序运行结果是1。(3)扩展到对三个运算符重载在String类体中声明三个重载函数是友元函数,并编写相应的函数。/本程序适用于VC+6.0#include#include class String public:String()p=NULL;String(char*str);friend bool operator (String&string1,String&string2);friend bool operator (String&string1,String&string2);friend bool operator=(
20、String&string1,String&string2);void display();private:char*p;String:String(char*str)p=str;void String:display()cout(String&string1,String&string2)if(strcmp(string1.p,string2.p)0)return true;else return false;bool operator(String&string1,String&string2)if(strcmp(string1.p,string2.p)0)return true;else
21、 return false;bool operator=(String&string1,String&string2)if(strcmp(string1.p,string2.p)=0)return true;else return false;int main()String string1(Hello),string2(Book),string3(Computer);coutstring2)endl;cout(string1string3)endl;cout(string1=string2)endl;return 0;运行结果为1004.6重载单目运算符重载单目运算符单目运行符只要一个操作数
22、,由于只有一个操作数,重载函数最多只有一个参数,假如将运算符重载函数定义为成员函数还可以不用参数。下面以自增运算符+为例,学习单目运算符的重载函数的编写方法。例4.5 有一个Time类,数据成员有时、分、秒。要求模拟秒表,每次走一秒,满60秒进位,秒又从零起先计数。满60分进位,分又从零起先计数。输出时、分和秒的值。#include using namespace std;class Time public:Time()hour=0;minute=0;sec=0;Time(int h,int m,int s):hour(h),minute(m),sec(s)Time operator+();v
23、oid display()couthour:minute:sec=60)sec=sec-60;minute+;if(minute=60)minute=minute-60;hour+;hour=hour%24;return*this;42 int main()Time time1(23,59,0);for(int i=0;i61;i+)+time1;time1.display();return 0;C+中除了有前+外,还有后+。同样的运算符由于操作数的位置不同,含义也不同。怎样区分前+和后+?C+给了一个方法,在自增或自减运算符重载函数中,增加一个int 形参。程序员可以选择带int形参的函数做
24、后+,也可以选择不带int形参的函数做前+。例4.6 在例4.5 的基础上增加后+运算符重载函数。#include using namespace std;class Time public:Time()hour=0;minute=0;sec=0;Time(int h,int m,int s):hour(h),minute(m),sec(s)Time operator+();Time operator+(int);void display()couthour:minute:sec=60)sec=sec-60;minute+;if(minute=60)minute=minute-60;hour+
25、;hour=hour%24;return*this;46分析:后+运算的含义是操作数先参与其他运算后再自加。如m=n+先将n的值赐予m,然后n再自加1。设计后+重载函数要遵循这个特性。Time Time:operator+(int)Time temp(*this);/保存修改前的对象做返回值 +(*this);return temp;int main()Time time1(21,34,59),time2;cout time1:;time1.display();+time1;cout+time1:;time1.display();time2=time1+;couttime1+:;time1.d
26、isplay();cout和移位运算符进行了重载,使它们分别成为流提取运算符和流插入运算符。用来输入或输出C+的标准类型数据,所以要用#include using namespace std;把头文件包含到程序中。用户自定义类型的数据不能干脆用输出和输入,如想用它们进行输入或输出,程序员必需对它们重载。重载函数原型的格式如下:istream&operator (istream&,自定义类&);ostream&operator 重载函数和重载函数只能定义为友元函数,不能定义为成员函数,因为函数有两个形参,并且第一个形参不是自定义类型。重载流插入运算符重载流插入运算符“”例4.7 在例4.2的基础
27、上用重载函数输出复数。分析:在类中声明重载函数是友元函数friend ostream&operator (ostream&,Complex&);在类外定义友元函数:ostream&operator (ostream&output,Complex&c)output(c.real+c.imagi)endl;return output;/本程序适用于本程序适用于VC+6.0#include int main()Complex c1(2,4),c2(6,10),c3;c3=c1+c2;coutc3;return 0;分析C+怎样处理”cout c3;”语句运算符的左边是ostream的对象cout,右
28、边是程序员自定义类complex的对象c3,语句符合运算符重载友元函数operator的形参类型要求,系统调用友元函数,C+把这个语句说明为:operator(cout,c3);通过形参引用传递,函数中的output就是cout,函数中的c就是c3,函数就变成:cout(c3.real+c3.imagi)endl;return cout;return cout 是将输出流现状返回。C+规定运算符”例4.8 在例4.7 的基础上增加流提取运算符重载函数,用cin输入复数,用cout(istream&,Complex&);在类外定义函数:istream&operator (istream&inpu
29、t,Complex&c)cout c.real c.imag;return input;int main()Complex c1,c2;cinc1c2;coutc1=c1endl;coutc2=c2重载函数中的形参input是istream类对象引用,在执行cinc1时,调用operator函数,将cin引用传递给input,input是cin的别名,同样c是c1的别名。因此,input c.real c.imag;相当于cin c1.real c1.imag。函数返回cin的新值。使程序可以用重载函数连续从输入流提取数据给complex类对象。程序逻辑上是正确的,但还有缺陷,假如输入的虚部是
30、负数时,输出的形式变成:c2=(4+-10i)在负数前多个正号。可以对程序稍做修改:ostream&operator (ostream&output,Complex&c)output(=0)output+;output c.imagi)endl;return output;从本章例子中可以留意到,在运算符重载中运用引用参数的重要性,用引用形参在调用函数时,通过传递地址方式让形参成为实参的别名,不用生成临时变量,削减了时间和空间的开销。此外,如重载函数的返回值是对象引用时,返回的是对象,它可以出现在赋值号的左侧而成为左值,可以被赋值或参与其他操作(如保留 cout流的当前值以便能连续运用输出)。
31、标准类型数据间的转换标准类型数据间的转换vint i=6;vi=7.5+i;v编译过程:7.5作为double型数处理求解表达式时,将6转换陈double,然后再和7.5相加得到的13.5的值向整型变量i赋值时,先转换为13再赋值这种转换是c+编译系统自动完成的,称为隐式类型转换。vC+的显式类型转换,是一种人为的强制转换。v格式:v类型名(数据)v例如:int(89.5)vC语言中的接受形式v(类型名)数据v如:(int)89.5vC+保留了c语言的用法,提倡用c+的方法用转换构造函数进行类型转换用转换构造函数进行类型转换v作用:将一个其它类型的数据转换成一个指定的类的对象。v转换构造函数只
32、有一个形参,如v Complex(double r)real=r;imag=0;v其作用就是将double型的参数r转换成Complex类的对象v Complex c1(3.5);/建立一个对象,调用构造函数v可以用声明语句建立一个无名的Complex对象,如vComplex(3.6)/合法,但无法运用v可以在一个表达式中运用无名对象,如vc1=complex(3.6);/假设c1已被定义为Complex类的对象v转换构造函数也是一种构造函数,它遵循构造函数的一般规则。v通常把有一个参数的构造函数用作类型转换,所以,称为转换构造函数。v留意:v转换构造函数只能有一个参数。假如有多个参数,就不是
33、转换构造函数。缘由是:假如有多个参数的话,原委是把哪个参数转换成类的对象呢v运用转换构造函数将一个指定的数据转换成类的对象的方法如下:v(1)先声明一个类(如上面的Complex)。v(2)在这个类中定义一个只有一个参数的构造函数,参数的类型是须要转换的类型,在函数体中指定转换的方法。v(3)在该类的作用域内可以用以下形式进行类型转换:v类名(指定类型的数据)不仅可以将一个标准类型数据转换成类对象,也可以将另一个类的对象转换成转换构造函数所在类的对象。如:可以将一个学生类对象转换成老师类对象,可以在Teacher类中写出下面的构造函数:Teacher(Student&s)num=s.num;s
34、trcpy(name,s.name);sex=s.sex;留意:对象s中的num,name,sex必需是公有成员,否则不能被类外引用。用类型转换函数进行类型转换用类型转换函数进行类型转换v作用:将一个类的对象转换成另一个类型的数据v格式:v operator 类型名()实现转换的语句v在函数名前面不能指定函数类型,函数没有参数v返回值的类型是由函数名中指定的类型名来确定的。类型转换函数只能作为成员函数,因为转换的主体是本类的对象。不能作为友元函数或一般函数。v转换构造函数和类型转换运算符的共同功能:v当须要的时候,编译系统会自动调用这些函数,建立一个无名的临时对象(或临时变量)v例:vdoub
35、le d1,d2;Complex c1,c2;v假设类中定义了类型转换函数v对于表达式:d1=d2+c1;编译系统发觉“+”左侧的d2是double型,而右侧的c1是Complex类对象,假如没有对运算符“+”进行重载,就会检查有无类型转换函数,结果发觉有对double的重载函数,就调用该函数,把Complex类对象c1转换为double型数据,建立一个临时的double变量,并与d2相加,最终将一个double型数据赋给d1。v假如类中已定义转换构造函数并重载了运算符“+”(作为Complex类的友元函数),但未对double定义类型转换函数(或者说未对double重载)对于表达式:vc2=
36、c1+d2;v编译系统怎样处理呢?v它发觉运算符“+”左侧的c1是Complex类对象,右侧的d2是double型。v编译系统找寻有无对“+”的重载,发觉有operator+函数,但它是Complex类的友元函数,要求两个Complex类的形参,即只能实现两个Complex类对象相加,而现在d2是double型,不合要求。v在类中没有对double进行重载,因此不行能把c1转换为double型数据然后相加。v编译系统就去找有无转换构造函数,发觉有,就调用转换构造函数Complex(d2),建立一个临时的Complex类对象,在调用operator+函数,将两个复数相加,然后赋给c2。v相当于执
37、行表达式:c2=c1+Complex(d2)v例4.9 运用类型转换函数的简洁例子v#include vusing namespace std;vclass Complexv public:v Complex()real=0;imag=0;v Complex(double r,double i)real=r;imag=i;v operator double()return real;v private:v double real;v double imag;v;v vint main()vComplex c1(3,4),c2(5,-10),c3;v double d;v d=2.5+c1;v
38、coutdendl;v return 0;v5.5v分析:v(1)假如在Complex类中没有定义类型转换函数operator double,程序编译将会出错。因为不能实现double数据类型与Complex类对象的相加。v(2)假如在main函数中加一语句:c3=c2;编译系统是把c2按Complex类对象处理,因为赋值号两侧为同一类数据,可以合法赋值。v(3)假如在Complex类中声明白重载运算符“+”函数作为友元函数,并在类外定义operator+函数:vComplex operator+(Complex c1,Complex c2)vreturn Complex(c1.real+c2
39、.real,c1.imag+c2.imag);vc3=c1+c2;/有运算符“+”重载,可以实现对象相加vd=c1+c2;/先相加,再通过对double的重载函数将临时对象转化为double,再赋值v对类型的重载和对运算符的重载的概念和方法相像v重载函数都运用关键字operator,它的意思是“运算符”。因此,通常把类型转换函数也成为类型转换运算符函数v由于它也是重载函数,因此也称为类型转换运算符重载函数(或称强制类型转换运算符重载函数)v例4.10包含转换构造函数、运算符重载函数和类型转换函数的程序v#include vusing namespace std;vclass Complexv
40、public:v Complex()real=0;imag=0;v Complex(double r)real=r;imag=0;v Complex(double r,double i)real=r;imag=i;v friend Complex operator+(Complex c1,Complex c2);v void display();v private:v double real;v double imag;v;v vComplex operator+(Complex c1,Complex c2)v return Complex(c1.real+c2.real,c1.imag+c2
41、.imag);v vvoid Complex:display()vcout(real+imagi)endl;vint main()vComplex c1(3,4),c2(5,-10),c3;v c3=c1+2.5;v c3.display();v return 0;vv分析:v(1)假如没有转换构造函数,则此程序编译出错,因为没有重载运算符使之能将Complex类对象与double数据相加。v(2)由于重载了运算符“+”,表达式c1+2.5,编译系统说明为:voperator+(c1,2.5)/2.5不是Complex类对象v调用转换构造函数Complex(2.5),形成表达式:voperat
42、or+(c1,Complex(2.5)(3)假如把“c3=c1+2.5”改为“c3=2.5+c1”。编译仍能通过。我们从中得到结论:在已定义了相应的转换构造函数状况下,将运算符“+”函数重载为友元函数,在进行两个复数相加时,可以运用交换律。假如运算符“+”重载函数不作为Complex类的友元函数,而作为Complex类的成员函数,不能满足交换律。函数的原型:operator+(Complex c2);/Complex类成员函数函数第一个参数省略了,它隐含指this所指的对象。对于表达式:c1+2.5c+编译系统说明为:c1.operator+(2.5),通过转换构造函数Complex(2.5)
43、,建立一个临时的Complex对象。则表达式相当于:c1.operator+(Complex(2.5).但对于表达式:2.5+c1。C+系统说明为(2.5).operator+(c1)/错误假如运算符函数重载为成员函数,它的第一个参数必需是本类的对象。当第一个操作数不是类对象时,不能将运算符函数重载为成员函数。假如将运算符“+”函数重载为成员函数,交换律不适用。一般状况下,将双目运算符函数重载为友元函数。单目运算符则为重载成员函数。v(4)在上面的基础上增加类型转换函数:Complex类的公有部分为:vpublic:v Complex()real=0;imag=0;v Complex(doub
44、le r)real=r;imag=0;v Complex(double r,double i)real=r;imag=i;v operator double()coutreturn real;v friend Complex operator+(Complex c1,Complex c2);v void display();v程序在编译时出错,缘由是出现二义性。在处理c1+2.5时出现二义性。一种理解是:调用转换构造函数,把2.5变成Complex类对象,然后调用运算符“+”重载函数,与c1进行复数相加。v另一种理解是:把c1转换成double型数,然后与2.5进行相加。系统无法判定,这两者是冲突的。v假如要运用类型转换函数,就应当删去运算符“+”重载函数。