谭浩强《C++程序设计》课件第10章.ppt
《谭浩强《C++程序设计》课件第10章.ppt》由会员分享,可在线阅读,更多相关《谭浩强《C++程序设计》课件第10章.ppt(78页珍藏版)》请在课桌文档上搜索。
1、第10章 运算符重载,10.1 什么是运算符重载10.2 运算符重载的方法10.3 重载运算符的规则10.4 运算符重载函数作为类成员函数和友元函数10.5 重载双目运算符10.6 重载单目运算符10.7 重载流插入运算符和流提取运算符10.8 不同类型数据间的转换,10.1 什么是运算符重载,所谓重载,就是重新赋予新的含义。函数重载就是对一个已有的函数赋予新的含义,使之实现新功能。运算符也可以重载。实际上,我们已经在不知不觉之中使用了运算符重载。现在要讨论的问题是:用户能否根据自己的需要对C+已提供的运算符进行重载,赋予它们新的含义,使之一名多用。譬如,能否用“+”号进行两个复数的相加。在C
2、+中不能在程序中直接用运算符“+”对复数进行相加运算。用户必须自己设法实现复数相加。例如用户可以通过定义一个专门的函数来实现复数相加。见例10.1。,例10.1 通过函数来实现复数相加。#include using namespace std;class Complex/定义Complex类public:Complex()real=0;imag=0;/定义构造函数Complex(double r,double i)real=r;imag=i;/构造函数重载Complex complex_add(Complex,c.imag=imag+c2.imag;return c;void Complexd
3、isplay()/定义输出函数cout(real,imagi)endl;int main()Complex c1(3,4),c2(5,-10),c3;/定义3个复数对象c3=plex_add(c2);/调用复数相加函数coutc1=;c1.display();/输出c1的值coutc2=;c2.display();/输出c2的值coutc1+c2=;c3.display();/输出c3的值return 0;运行结果如下:c1=(3+4i)c2=(5-10i)c1+c2=(8,-6i),结果无疑是正确的,但调用方式不直观、太烦琐,使人感到很不方便。能否也和整数的加法运算一样,直接用加号“+”来实
4、现复数运算呢?如c3=c1+c2;编译系统就会自动完成c1和c2两个复数相加的运算。如果能做到,就为对象的运算提供了很大的方便。这就需要对运算符“+”进行重载。,10.2 运算符重载的方法,运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。重载运算符的函数一般格式如下:函数类型 operator 运算符名称(形参表列)对运算符的重载处理 例如,想将“+”用于Complex类(复数)的加法运算,函数的原型可以是这样的:Complex operator+(Compl
5、ex,在定义了重载运算符的函数后,可以说:函数operator+重载了运算符+。为了说明在运算符重载后,执行表达式就是调用函数的过程,可以把两个整数相加也想像为调用下面的函数:int operator+(int a,int b)return(a+b);如果有表达式5+8,就调用此函数,将5和8作为调用函数时的实参,函数的返回值为13。这就是用函数的方法理解运算符。可以在例10.1程序的基础上重载运算符“+”,使之用于复数相加。,例10.2 改写例10.1,重载运算符“+”,使之能用于两个复数相加。#include using namespace std;class Complexpublic:
6、Complex()real=0;imag=0;Complex(double r,double i)real=r;imag=i;Complex operator+(Complex,return c;void Complexdisplay()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;运行结果与例10.1相同:c1=(3+4i)c2=(
7、5-10i)c1+c2=(8,-6i),请比较例10.1和例10.2,只有两处不同:(1)在例10.2中以operator+函数取代了例10.1中的complex_add函数,而且只是函数名不同,函数体和函数返回值的类型都是相同的。(2)在main函数中,以“c3=c1+c2;”取代了例10.1中的“c3=plex_add(c2);”。在将运算符+重载为类的成员函数后,C+编译系统将程序中的表达式c1+c2解释为c1.operator+(c2)/其中c1和c2是Complex类的对象即以c2为实参调用c1的运算符重载函数operator+(Complex&c2),进行求值,得到两个复数之和。,
8、虽然重载运算符所实现的功能完全可以用函数实现,但是使用运算符重载能使用户程序易于编写、阅读和维护。在实际工作中,类的声明和类的使用往往是分离的。假如在声明Complex类时,对运算符+,-,*,/都进行了重载,那么使用这个类的用户在编程时可以完全不考虑函数是怎么实现的,放心大胆地直接使用+,-,*,/进行复数的运算即可,十分方便。对上面的运算符重载函数operator+还可以改写得更简练一些:Complex Complexoperator+(Complex 需要说明的是:运算符被重载后,其原有的功能仍然保留,没有丧失或改变。,通过运算符重载,扩大了C+已有运算符的作用范围,使之能用于类对象。运
9、算符重载对C+有重要的意义,把运算符重载和类结合起来,可以在C+程序中定义出很有实用意义而使用方便的新的数据类型。运算符重载使C+具有更强大的功能、更好的可扩充性和适应性,这是C+最吸引人的特点之一。,10.3 重载运算符的规则,(1)C+不允许用户自己定义新的运算符,只能对已有的C+运算符进行重载。(2)C+允许重载的运算符C+中绝大部分的运算符允许重载。具体规定见书中表10.1。不能重载的运算符只有5个:.(成员访问运算符).*(成员指针访问运算符)(域运算符)sizeof(长度运算符)?:(条件运算符),前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof运算符
10、的运算对象是类型而不是变量或一般表达式,不具重载的特征。(3)重载不能改变运算符运算对象(即操作数)的个数。(4)重载不能改变运算符的优先级别。(5)重载不能改变运算符的结合性。(6)重载运算符的函数不能有默认的参数,否则就改变了运算符参数的个数,与前面第(3)点矛盾。(7)重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用)。也就是说,参数不能全部是C+的标准类型,以防止用户修改用于标准类型数据的运算符的性质。,(8)用于类对象的运算符一般必须重载,但有两个例外,运算符“=”和“&”不必用户重载。赋值运算符(=)可以用于每一个类对象,可以利用它在
11、同类对象之间相互赋值。地址运算符&也不必重载,它能返回类对象在内存中的起始地址。(9)应当使重载运算符的功能类似于该运算符作用于标准类型数据时所实现的功能。(10)运算符重载函数可以是类的成员函数(如例10.2),也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数。,10.4 运算符重载函数作为类成员函数和友元函数,在本章例10.2程序中对运算符“+”进行了重载,使之能用于两个复数的相加。在该例中运算符重载函数operator+作为Complex类中的成员函数。“+”是双目运算符,为什么在例10.2程序中的重载函数中只有一个参数呢?实际上,运算符重载函数有两个参数,由于重载
12、函数是Complex类中的成员函数,有一个参数是隐含的,运算符函数是用this指针隐式地访问类对象的成员。,可以看到,重载函数operator+访问了两个对象中的成员,一个是this指针指向的对象中的成员,一个是形参对象中的成员。如this-real+c2.real,this-real就是c1.real。在10.2节中已说明,在将运算符函数重载为成员函数后,如果出现含该运算符的表达式,如c1+c2,编译系统把它解释为c1.operator+(c2)即通过对象c1调用运算符重载函数,并以表达式中第二个参数(运算符右侧的类对象c2)作为函数实参。运算符重载函数的返回值是Complex类型,返回值是
13、复数c1和c2之和(Complex(c1.real+c2.real,c1.imag+c2.imag)。,运算符重载函数除了可以作为类的成员函数外,还可以是非成员函数。可以将例10.2改写为例10.3。例10.3 将运算符“+”重载为适用于复数加法,重载函数不作为成员函数,而放在类外,作为Complex类的友元函数。#include using namespace std;class Complexpublic:Complex()real=0;imag=0;Complex(double r,double i)real=r;imag=i;friend Complex operator+(Compl
14、ex,;Complex operator+(Complex,与例10.2相比较,只作了一处改动,将运算符函数不作为成员函数,而把它放在类外,在Complex类中声明它为友元函数。同时将运算符函数改为有两个参数。在将运算符“+”重载为非成员函数后,C+编译系统将程序中的表达式c1+c2解释为operator+(c1,c2)即执行c1+c2相当于调用以下函数:Complex operator+(Complex 求出两个复数之和。运行结果同例10.2。为什么把运算符函数作为友元函数呢?因为运算符函数要访问Complex类对象中的成员。如果运算符函数不是Complex类的友元函数,而是一个普通的函数,
15、它是没有权利访问Complex类的私有成员的。,在10.2节中曾提到过:运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数。现在分别讨论这3种情况。首先,只有在极少的情况下才使用既不是类的成员函数也不是友元函数的普通函数,原因是上面提到的,普通函数不能直接访问类的私有成员。在剩下的两种方式中,什么时候应该用成员函数方式,什么时候应该用友元函数方式?二者有何区别呢?如果将运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少写一个函数的参数。但必须要求运算表达式第一个参数(即运算符左侧的操作数)是一个类对象,,
16、而且与运算符函数的类型相同。因为必须通过类的对象去调用该类的成员函数,而且只有运算符重载函数返回值与该对象同类型,运算结果才有意义。在例10.2中,表达式c1+c2中第一个参数c1是Complex类对象,运算符函数返回值的类型也是Complex,这是正确的。如果c1不是Complex类,它就无法通过隐式this指针访问Complex类的成员了。如果函数返回值不是Complex类复数,显然这种运算是没有实际意义的。如想将一个复数和一个整数相加,如c1+i,可以将运算符重载函数作为成员函数,如下面的形式:Complex Complexoperator+(int,注意在表达式中重载的运算符“+”左侧
17、应为Complex类的对象,如c3=c2+i;不能写成c3=i+c2;/运算符“+”的左侧不是类对象,编译出错如果出于某种考虑,要求在使用重载运算符时运算符左侧的操作数是整型量(如表达式i+c2,运算符左侧的操作数i是整数),这时是无法利用前面定义的重载运算符的,因为无法调用i.operator+函数。可想而知,如果运算符左侧的操作数属于C+标准类型(如int)或是一个其他类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数。如果函数需要访问类的私有成员,则必须声明为友元函数。可以在Complex类中声明:,friend Complex operator+(int/错误,类型不匹配,
18、请注意,数学上的交换律在此不适用。如果希望适用交换律,则应再重载一次运算符“+”。如Complex operator+(Complex 这样,使用表达式i+c2和c2+i都合法,编译系统会根据表达式的形式选择调用与之匹配的运算符重载函数。可以将以上两个运算符重载函数都作为友元函数,也可以将一个运算符重载函数(运算符左侧为对象名的)作为成员函数,另一个(运算符左侧不是对象名的)作为友元函数。但不可能将两个都作为成员函数,原因是显然的。C+规定,有的运算符(如赋值运算符、下标运算符、函数调用运算符)必须定义为类的成员函数,有的运算符则不能定义为类的成员函数(如流插入“”、类型转换运算符)。,由于友
19、元的使用会破坏类的封装,因此从原则上说,要尽量将运算符函数作为成员函数。但考虑到各方面的因素,一般将单目运算符重载为成员函数,将双目运算符重载为友元函数。在学习了本章第10.7节例10.9的讨论后,读者对此会有更深入的认识。说明:有的C+编译系统(如Visual C+6.0)没有完全实现C+标准,它所提供不带后缀.h的头文件不支持把成员函数重载为友元函数。上面例10.3程序在GCC中能正常运行,而在Visual C+6.0中会编译出错。但是Visual C+所提供的老形式的带后缀.h的头文件可以支持此项功能,因此可以将程序头两行修改如下,即可顺利运行:#include 以后如遇到类似情况,亦可
20、照此办理。,10.5 重载双目运算符,双目运算符(或称二元运算符)是C+中最常用的运算符。双目运算符有两个操作数,通常在运算符的左右两侧,如3+5,a=b,i10等。在重载双目运算符时,不言而喻在函数中应该有两个参数。下面再举一个例子说明重载双目运算符的应用。,例10.4 定义一个字符串类String,用来存放不定长的字符串,重载运算符“=”,“”,用于两个字符串的等于、小于和大于的比较运算。为了使读者便于理解程序,同时也使读者了解建立程序的步骤,下面分几步来介绍编程过程。(1)先建立一个String类:#include using namespace std;class Stringpubl
21、ic:String()p=NULL;/默认构造函数String(char*str);/构造函数void display();private:char*p;/字符型指针,用于指向字符串;,StringString(char*str)/定义构造函数p=str;/使p指向实参字符串void Stringdisplay()/输出p所指向的字符串coutp;int main()String string1(Hello),string2(Book);string1.display();coutendl;string2.display();return 0;运行结果为HelloBook,(2)有了这个基础后
22、,再增加其他必要的内容。现在增加对运算符重载的部分。为便于编写和调试,先重载一个运算符“”。程序如下:#include#include using namespace std;class Stringpublic:String()p=NULL;String(char*str);friend bool operator(String,void Stringdisplay()/输出p所指向的字符串cout(String 程序运行结果为1。这只是一个并不很完善的程序,但是,已经完成了实质性的工作了,运算符重载成功了。其他两个运算符的重载如法炮制即可。,(3)扩展到对3个运算符重载。在String类体
23、中声明3个成员函数:friend bool operator(String else,return false;bool operator=(String,运行结果为100结果显然是对的。到此为止,主要任务基本完成。(4)再进一步修饰完善,使输出结果更直观。下面给出最后的程序。#include using namespace std;class Stringpublic:String()p=NULL;String(char*str);friend bool operator(String,void display();private:char*p;StringString(char*str)p
24、=str;void Stringdisplay()/输出p所指向的字符串cout(String bool operator(String&string1,String&string2),if(strcmp(string1.p,string2.p)(string1,string2)=1)string1.display();cout;string2.display();elseif(operator(string1,string2)=1),string1.display();coutBookBookComputerHello=Hello,增加了一个compare函数,用来对两个字符串进行比较,并输
25、出相应的信息。这样可以减轻主函数的负担,使主函数简明易读。通过这个例子,不仅可以学习到有关双目运算符重载的知识,而且还可以学习怎样去编写C+程序。这种方法的指导思想是:先搭框架,逐步扩充,由简到繁,最后完善。边编程,边调试,边扩充。千万不要企图在一开始时就解决所有的细节。类是可扩充的,可以一步一步地扩充它的功能。最好直接在计算机上写程序,每一步都要上机调试,调试通过了前面一步再做下一步,步步为营。这样编程和调试的效率是比较高的。读者可以试验一下。,10.6 重载单目运算符,单目运算符只有一个操作数,如!a,-b,&c,*p,还有最常用的+i和-i等。重载单目运算符的方法与重载双目运算符的方法是
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+程序设计 谭浩强 C+ 程序设计 课件 10
链接地址:https://www.desk33.com/p-263057.html