面向对象程序设计 ch10.ppt
《面向对象程序设计 ch10.ppt》由会员分享,可在线阅读,更多相关《面向对象程序设计 ch10.ppt(92页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第第10章章运算符重载及流类库运算符重载及流类库在建立了自己的类以后,在建立了自己的类以后,C+允许程序员重新定义允许程序员重新定义C+中中已有的运算符,通过运算符重载已有的运算符,通过运算符重载,就可像处理基本数据类就可像处理基本数据类型那样使用它们。型那样使用它们。为了面向对象编程的需要,为了面向对象编程的需要,C+提供了一个用于输入输出提供了一个用于输入输出(I/O)操作的类体系,这个类体系提供了对预定义类型进操作的类体系,这个类体系提供了对预定义类型进行行I/O操作的能力,程序员也可以利用这个类体系进行自操作的能力,程序员也可以利用这个类体系进行自定义类型的定义类型的I/O操作。操作。
2、本章将简要介绍运算符重载的基础知识、流类库的概念及本章将简要介绍运算符重载的基础知识、流类库的概念及使用流类库进行文件存取的基本方法。使用流类库进行文件存取的基本方法。主要内容主要内容10.1运算符重载运算符重载10.2流类库流类库10.3文件流文件流10.4文件读写综合实例文件读写综合实例10.1运算符重载运算符重载本节首先引入运算符重载的必要性,然后讨论类运算符和本节首先引入运算符重载的必要性,然后讨论类运算符和友元运算符的异同。友元运算符的异同。10.1.1重载对象的赋值运算符重载对象的赋值运算符编译器在默认情况下为每个类生成一个默认的赋值操作,编译器在默认情况下为每个类生成一个默认的赋
3、值操作,用于同类的两个对象之间相互赋值。默认的含义是逐个为用于同类的两个对象之间相互赋值。默认的含义是逐个为成员赋值,即将一个对象的成员的值赋给另一个对象相应成员赋值,即将一个对象的成员的值赋给另一个对象相应的成员,这种赋值方式对于有些类可能是不正确的。假设的成员,这种赋值方式对于有些类可能是不正确的。假设类类str的数据成员是的数据成员是“char*st”,下面语句下面语句strs1(hello),s2(world);s2=s1;经赋值后,经赋值后,s2.st和和s1.st是同一块存储地址(如图是同一块存储地址(如图7.1所示)。所示)。当当s2和和s1的生存期结束时,存储的生存期结束时,存
4、储“hello”的变量被删除的变量被删除2次,这是个严重的错误。另外,对于次,这是个严重的错误。另外,对于“s1=s1”的情况,的情况,也应不执行赋值操作。因此,程序必须为也应不执行赋值操作。因此,程序必须为str类定义自己的类定义自己的赋值操作赋值操作“=”。这个操作应该能够保证。这个操作应该能够保证s1.st=s2.st,但两但两者各自具有自己的存储地址(如图者各自具有自己的存储地址(如图7.2所示)。如果发现所示)。如果发现本身自己赋值,则不执行赋值操作。先不管如何声明这个本身自己赋值,则不执行赋值操作。先不管如何声明这个“=”函数,使用函数,使用“赋值函数赋值函数”一词代表这个操作,则
5、可一词代表这个操作,则可像下面这样实现它:像下面这样实现它:str&str:赋值函数赋值函数(str&a)if(this=&a)/防止防止a=a这样的赋值这样的赋值return*this;/a=a,退出退出deletest;st=newcharstrlen(a.st)+1;/申请内存申请内存strcpy(st,a.st);/将对象将对象a的数据复制一份到的数据复制一份到/申请的内存区申请的内存区return*this;/返回返回this指针指向的对象指针指向的对象这个成员函数必须使用引用参数,这个成员函数必须使用引用参数,“赋值函数赋值函数”使用符号使用符号“operator=”表示,表示,C
6、+的关键字的关键字“operator”和运算和运算符一起使用,表示一个运算符函数,例如符一起使用,表示一个运算符函数,例如“operator+”表示重载表示重载“+”运算符。读者应将运算符。读者应将operator=从整体上视为从整体上视为一个(运算符)函数名。运算符函数将在后续章节介绍。一个(运算符)函数名。运算符函数将在后续章节介绍。这样就可将它声明为这样就可将它声明为“str&stroperator=(str&);”,即即函数函数operator=(str&)返回返回str类对象的引用。定义时记作:类对象的引用。定义时记作:str&str:operator=(str&a)。当当str类定
7、义了赋值运算符函数之后,类定义了赋值运算符函数之后,“operator=”是类是类的成员函数名,对象的成员函数名,对象s2调用这个成员函数,函数的参数是调用这个成员函数,函数的参数是s1。写成函数调用形式如下:写成函数调用形式如下:s2.operator=(s1);这虽然是正规的函数调用写法,但要实现的是这虽然是正规的函数调用写法,但要实现的是“=”作用,作用,所以系统允许直接写成如下形式的语句:所以系统允许直接写成如下形式的语句:s2=s1;这将被这将被C+编译解释为编译解释为s2.operator=(s1);即即s2调用成员函数调用成员函数str:operator=(str&)完成赋值操作
8、。完成赋值操作。因为这个函数返回一个引用,所以它可以用于下面这种赋因为这个函数返回一个引用,所以它可以用于下面这种赋值操作中:值操作中:s3=s2=s1;C+编译器将其解释为编译器将其解释为s3.operator=(s2.operator=(s1);下面给出下面给出str类的完整实现和测试主程序,以便读者对比分类的完整实现和测试主程序,以便读者对比分析。析。由此可见,它们虽然是函数,但完全可以不写成函数调用,由此可见,它们虽然是函数,但完全可以不写成函数调用,而采用原来的书写习惯,系统会自动按其真正的含义执行。而采用原来的书写习惯,系统会自动按其真正的含义执行。运算符重载其实就是函数重载,抓住
9、这个实质,就很容易运算符重载其实就是函数重载,抓住这个实质,就很容易理解了。理解了。【例【例10.1】完整实现完整实现str类的例子。类的例子。#include#includeusingnamespacestd;classstrprivate:char*st;public:str(char*s);str(str&s);str&operator=(str&a);str&operator=(char*s);voidprint()coutst这这4个运算符只能用个运算符只能用类运算符来重载。类运算符来重载。10.1.3和和+运算符重载实例运算符重载实例为了加深理解,本节再结合函数调用,分别给出使用友
10、元为了加深理解,本节再结合函数调用,分别给出使用友元运算符和类运算符的例子。运算符和类运算符的例子。其实,插入符其实,插入符“”的重载也与其他运算的重载也与其他运算符重载一样,只是它们必须作为类的友元重载,因为操作符重载一样,只是它们必须作为类的友元重载,因为操作符的左边是流而不是被操作的对象。除非违反习惯将流放符的左边是流而不是被操作的对象。除非违反习惯将流放在操作符的右边,但这样既会影响程序的可读性,也违反在操作符的右边,但这样既会影响程序的可读性,也违反了操作符重载的原则。了操作符重载的原则。插入符函数的一般形式为:插入符函数的一般形式为:ostream&operator(istream
11、&函数的流函数的流,类名类名&对象名对象名)/函数代码函数代码return函数的流函数的流;另外,提取符函数需要返回新的值,所以应该使用引用,另外,提取符函数需要返回新的值,所以应该使用引用,即即“类名类名&对象名对象名”,不能使用,不能使用“类名类名对象名对象名”。插入符。插入符函数不返回值,所以两种方法都可以。函数不返回值,所以两种方法都可以。【例【例10.2】使用友元函数重载运算符使用友元函数重载运算符“”。#includeclasstestprivate:inti;floatf;charch;public:test(inta=0,floatb=0,charc=0)i=a;f=b;ch=
12、c;friendostream&operator(istream&,test&);ostream&operator(ostream&stream,testobj)streamobj.i,;streamobj.f,;streamobj.ch(istream&t_stream,test&obj)t_streamobj.i;t_streamobj.f;t_streamobj.ch;returnt_stream;voidmain()testA(45,8.5,W);operator(cout,A);testB,C;cout(cin,B);operator(cin,C);operator(cout,B);
13、operator(cout,C);运行示例如下:运行示例如下:45,8.5,WInputasifch:55.8A23.4a/假设输入两组假设输入两组5,5.8,A2,3.4,a将主函数写成上面的函数调用形式,是为了演示运算符就将主函数写成上面的函数调用形式,是为了演示运算符就是函数重载。一般在使用时,则直接使用运算符。下面是是函数重载。一般在使用时,则直接使用运算符。下面是正规的使用方式:正规的使用方式:voidmain()testA(45,8.5,W);coutA;testB,C;coutBC;coutBC;显然,运算符显然,运算符“”重载函数有两个参数,第重载函数有两个参数,第1个是个是o
14、stream类的一个引用,第类的一个引用,第2个是自定义类型的一个对象。个是自定义类型的一个对象。这个重载方式是友元重载。另外,这个函数的返回类型是这个重载方式是友元重载。另外,这个函数的返回类型是一个一个ostream类型的引用,在函数中实际返回的是该函数类型的引用,在函数中实际返回的是该函数的第的第1个参数,这样做是为了使得个参数,这样做是为了使得“”能够连续使用。能够连续使用。例如,对于语句例如,对于语句coutab;/a,b均为自定义类型的对象均为自定义类型的对象第第1次,系统把次,系统把couta作为作为operator(cout,a);来处理,返回来处理,返回cout,紧接着又把刚
15、返回的紧接着又把刚返回的cout连同后面的连同后面的“b”一起作为一起作为operator(cout,b);处理,再返回处理,再返回cout,从而实现了运算符从而实现了运算符“”的连续使用。的连续使用。【例【例10.3】使用类运算符重载使用类运算符重载+运算符。运算符。#includeusingnamespacestd;classnumberintnum;public:number(inti)num=i;intoperator+();/前缀:前缀:+nintoperator+(int);/后缀:后缀:n+voidprint()coutnum=numend;intnumber:operator+
16、()num+;returnnum;intnumber:operator+(int)/不用给出形参名不用给出形参名inti=num;num+;returni;voidmain()numbern(10);inti=+n;/i=11,n=11couti=iendl;/输出输出i=11n.print();/输出输出n=11i=n+;/i=11,n=12couti=iendl;/输出输出i=11n.print();/输出输出n=12同理,如果主函数的第同理,如果主函数的第2条和第条和第5条语句使用函数调用方式,条语句使用函数调用方式,则分别为:则分别为:inti=n.operator+();i=n.op
17、erator+(0);由此可见,只要定义正确,不必再使用函数调用方式,而由此可见,只要定义正确,不必再使用函数调用方式,而直接使用运算符。直接使用运算符。【例【例10.4】使用友元运算符重载使用友元运算符重载+运算符。运算符。为友元运算符需要要修改操作数,所以必须使用引用参数。为友元运算符需要要修改操作数,所以必须使用引用参数。程序如下:程序如下:#includeusingnamespacestd;classnumberintnum;public:number(inti)num=i;friendintoperator+(number&);/前缀:前缀:+nfriendintoperator+(
18、number&,int);/后缀:后缀:n+voidprint()coutnum=numendl;intoperator+(number&a)a.num+;returna.num;intoperator+(number&a,int)/不用给出不用给出int类型的形参类型的形参名名inti=a.num+;returni;仍然使用上面的主程序,则运行结果一样。仍然使用上面的主程序,则运行结果一样。有些有些C+编译器不区分前辍或后辍运算符,这时只能通过编译器不区分前辍或后辍运算符,这时只能通过对运算符函数进行重载时来反映其为前辍或后辍运算符。对运算符函数进行重载时来反映其为前辍或后辍运算符。注意不能
19、够自己定义新的运算符,而只能是把注意不能够自己定义新的运算符,而只能是把C+原有的原有的运算符用到自己设计的类上面去。同时,经过重载,运算运算符用到自己设计的类上面去。同时,经过重载,运算符并不改变原有的优先级,也不改变它所需的操作数数目。符并不改变原有的优先级,也不改变它所需的操作数数目。当不涉及到定义的类对象的时候,它仍然执行系统预定义当不涉及到定义的类对象的时候,它仍然执行系统预定义的运算,只有用到自己定义的对象上,才执行新定义的操的运算,只有用到自己定义的对象上,才执行新定义的操作。作。应该根据需要进行运算符重载。不排除在某些特殊情况下应该根据需要进行运算符重载。不排除在某些特殊情况下
20、会有一些特殊的需要,但大多数情况下不会将运算符会有一些特殊的需要,但大多数情况下不会将运算符“+”重载为两个复数相减的运算重载为两个复数相减的运算(尽管有能力这么做尽管有能力这么做)。一般总是要求运算符重载合乎习惯。一般总是要求运算符重载合乎习惯。尽管尽管C+有那么多运算符可以重载,但实际中真正需要去有那么多运算符可以重载,但实际中真正需要去重载的运算符却没有几个。所介绍的重载的运算符却没有几个。所介绍的“+”重载,结论重载,结论对对“-”完全适用。完全适用。举一反三,四则运算就不会有什么问题了。举一反三,四则运算就不会有什么问题了。10.1.4类运算符和友元运算符的区别类运算符和友元运算符的
21、区别如果运算符所需的操作数,尤其是第一个操作数希望进行如果运算符所需的操作数,尤其是第一个操作数希望进行隐式类型转换,则该运算符应该通过友元来重载。另一方隐式类型转换,则该运算符应该通过友元来重载。另一方面,如果一个运算符的操作需要修改类对象的状态,则应面,如果一个运算符的操作需要修改类对象的状态,则应当使用类运算符,这样更符合数据封装的要求。但参数是当使用类运算符,这样更符合数据封装的要求。但参数是使用引用还是对象,则要根据运算符在使用中可能出现的使用引用还是对象,则要根据运算符在使用中可能出现的情况来决定。例如,对复数类使用友元函数重载情况来决定。例如,对复数类使用友元函数重载“+”运运算
22、符,可以写出如下格式:算符,可以写出如下格式:friendcomplexoperator+(形参形参1,形参,形参2)/函数体定义函数体定义关键字关键字friend把把complexoperator+(形参形参1,形参,形参2)说明成说明成类的友元,使得它能够访问类类的友元,使得它能够访问类complex的私有数据。形参的私有数据。形参可以都是对象或者对象的引用,也可以一个为对象一个为可以都是对象或者对象的引用,也可以一个为对象一个为引用,这要视具体问题而定。引用,这要视具体问题而定。下面仅仅给出使用对象和引用的两种函数原型声明。下面仅仅给出使用对象和引用的两种函数原型声明。friendcom
23、plexoperator+(complex,complex);/对象对象/对象引用对象引用friendcomplexoperator+(complex&,complex&);其实,它们函数体的定义是一样的。其实,它们函数体的定义是一样的。/使用对象的定义使用对象的定义complexoperator+(complexc1,complexc2)returncomplex(c2.real+c1.real,c2.imag+c1.imag);/使用引用定义使用引用定义complexoperator+(complex&c1,complex&c2)returncomplex(c2.real+c1.real,
24、c2.imag+c1.imag);定义的方式也很灵活,例如也可以使用如下引用定义定义的方式也很灵活,例如也可以使用如下引用定义的方法:的方法:complexoperator+(complex&c1,complex&c2)doubler=c2.real+c1.real;doublei=c2.imag+c1.imag;returncomplex(r,i);【例例10.5】使用对象作为友元函数参数来定义运算符使用对象作为友元函数参数来定义运算符“+”的例子。的例子。因为因为C+定义了复数模板,所以这里不使用命名空间,包定义了复数模板,所以这里不使用命名空间,包含的是含的是iostream.h文件。文
25、件。#includeclasscomplexprivate:doublereal,imag;public:complex(doubler=0,doublei=0)real=r;imag=i;friendcomplexoperator+(complex,complex);voidshow()coutreal+imagi;complexoperator+(complexa,complexb)doubler=a.real+b.real;doublei=a.imag+b.imag;returncomplex(r,i);voidmain()complexx(5,3),y;y=x+7;y=7+y;y.sh
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 面向对象程序设计 ch10 面向 对象 程序设计
限制150内