汇天地之长育学子精英C++面向对象程序设计教程(下)第章多态性Chapter5Polymorphism第章模板与异常处理Chapter6Template第章流Chapter7stream35673所谓多态性就是不同对象收到相同的消息时,产生不同的动作。通俗的说,多态性是指用一个名字定义不同的函数,这些函数执行不同但又有类似的操作,即用同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”。以前讲的“函数重载”,就是一种多态。45.1运算符重载intx,y,z;z=x+y;这是将两个整数相加的方法,非常简单.若有一个复数类complexclasscomplex{public:doublereal;doubleimag;complex(doubler=0,doublei=0){real=r;imag=i;}};5把complex的两个对象com1和com2加在一起。能否这样实现:complexcom1(1.1,2.2),com2(3.3,4.4),total1;total1=com1+com2;C++为运算符提供了一种方法,即在进行运算符重载时,必须写一个运算符函数,其名字为operator后随一个要重载的运算符。6函数功能operator+()加法operator-()减法operator*()乘法operator/()除法operator()小于……表5.1运算符函数这样,编译器在一个运算符的两边“看”到自定义的数据类型,就执行型用户自己的函数,而不是内部运算符的常规程序.7classcomplex{public:doublereal;doubleimag;complex(doubler=0,doublei=0){real=r;imag=i;}};complexoperator+(complexco1,complexco2){complextemp;temp.real=co1.real+co2.real;temp.imag=co1.imag+co2.imag;returntemp;}voidmain(){complexcom1(1.1,2.2),com2(3.3,4.4),total1,total2;total1=operator+(com1,com2);//显示调用couttotal1.real+total1.imagi;total2=com1+com2;//隐式调用couttotal2.real+total2.imagi;}8注意:(1)重载运算符要与原有功能类似。(2)重载运算符,只能重载已有的运算符.程序员不能臆造新的运算符来扩充C++语言。(3)类属关系运算符“.”、指针运算符“*”、作用域运算符“::”、sizeof运算符不能被重载。(4)不能改变运算符的操作数个数。例如“+”,只能是两个操作数。(5)不能改变运算符原有的优先级。x=y-a*b;9例:用*实现两个复数相乘编程练习classcomplex{private:doublereal;doubleimag;public:complex(doubler=0,doublei=0){real=r;imag=i;}};思考:如何实现运算符重载呢?105.2友元运算符函数运算符重载函数是在类的外部定义的,这个运算的重载函数只能访问类中的公有数据成员,而不访问类的私有数据成员。解决办法是:(1)定义为它将要操作的类的成员函数(2)是定义类的友元函数。友元运算符函数定义的语法形式:classx{friend返回类型operator运算符(形参表)};11classcomplex{private:doublereal;doubleimag;public:complex(doubler=0,doublei=0){real=r;imag=i;}voidprint();friendcomplexoperator+(complexco1,complexco2);};complexoperator+(complexco1,complexco2){complextemp;temp.real=co1.real+co2.real;temp.imag=co1.imag+co2.imag;returntemp;}voidcomplex::print(){coutreal;if(imag0)cout+;if(imag!=0)coutimagi;}voidmain(){complexcom1(1.1,2.2),com2(3.3,4.4),total1,total2;total1=com1+com2;total1.print();}12以上程序在vc6中运行会出现如下错误:提示不能访问私有成员,没有这个访问权限。这是VC的一个经典BUG和namespace也有关.头文件换为:#includeiostreamusingstd::cin;usingstd::cout;usingstd::ostream;usingstd::istream;usingstd::endl;方法一:去掉usingnamespacestd;换成更小的名字空间。方法二:提前声明。最前面加上下面声明语句(红色部分),其余不变。#includeiostream#includestringusingnamespacestd;classcomplex;complexoperator+(complexco1,complexco2);方法三:头文件写为#includeiostream.h“这是因为iostream.h这个文件没有Bug,而iostream有Bug13例:用+-*/实现两个复数类的加减乘除。编程练习14成员运算符函数定义的语法形式classX{//…返回类型operator运算符(形参表);//…};返回类型X::operator运算符(形参表){函数体}5.3成员运算符函数1.单目运算符重载在成员运算符函数的形参表中,若运算符是单目的,则参数表为空;若运算符是双目的,则参数表中有一个操作数.2.双目运算符重载对双目运算符而言,成员运算符函数的形参表仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。15classcomplex{private:doublereal,imag;public:complex(doubler=0,doublei=0){real=r;imag=i;}voidprint();complexoperator+(complexco2);};complexcomplex::operator+(complexco2){complextemp;temp.real=real+co2.real;temp.imag=imag+co2.imag;returntemp;}voidcomplex::print(){coutreal;if(imag0)cout+;if(imag!=0)coutimagi;}voidmain(){complexcom1(1.1,2.2),com2(3.3,4.4),total1,total2;total1=com1+com2;total1.print();}16编程1:用成员,*实现两个复数相乘”编程练习175.4成员运算符函数与友元运算符函数的比较(1)对双目运算符而言,成员运算符函数带一个参数,而友元运算符函数带两个参数。对单目运算符而言,成员运算符函数不带参,而友元运算符函数带一个参数。(2)双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但有一种情况,必须使用友元函数。complexcomplex::operator+(intx){complextemp;temp.real=real+x;temp.imag=imag;returntemp;}main(){complexob;ob=ob+10;}main(){complexob;ob=10+ob;}18第二种写法不能工作的原因是运算符的左侧数是一个整数,而整数是一个内部类型数据10不能产生对成员运算符函数的调用。如果用两个友元函数来重载运算符函数“+”,就能消除由于“+”的左操作数是内部数据类型而带来的问题。练习:用友元实现上例两种赋值。19总结:C++大部分运算符既可说明为成员运算符函数,又可说明为友元运算符函数。一般而言,对于双目运算符,将它重载为一个友元运算符函数比重载为一个成员运算符函数便于使用。若一个运算符的操作需要修改类对象的状态,则选择成员运算符函数较好。建议:单目运算符,建议选择成员函数.=()[]-只能作为成员函数。双目运算符,建议重载为友元函数。205.5“++”和“--”的重载我们知道,运算符“++”和“--”放置在变量的前面与后面,其作用是有区别的。对于前缀方式++ob,可以定义成员运算符函数为:classX{public:Xoperator++();};对于后缀方式ob++,可以定义成员运算符函数为:classX{public:Xoperator++(int);};21classcomplex{private:doublereal;doubleimag;public:complex(doubler=0,doublei=0){real=r;imag=i;}voidprint();complexoperator++();complexoperator++(int);};complexcomplex::operator++(){complextemp;temp.real=++real;temp.imag=++imag;returntemp;}complexcomplex::operator++(int){complextemp;temp.real=real++;temp.imag=imag++;returntemp;}voidcomplex::print(){coutreal;if(imag0)cout+;if(imag!=0)coutimagi;}voidmain(){complexcom1(1.1,2.2),total1;total1=com1++;total1.print();}22对于前缀方式++ob,也可以定义友元运算符函数为:classX{public:friendXoperator++(X&ob);};对于后缀方式ob++,可以定义友元运算符函数为:classX{public:friendXoperator++(X&ob,int);};23练习:1、请用成员函数重载--(前缀方式),请用友元函数重载--(后缀方式)2、针对类Cstring,重载运算符”+”,使该运算符实现两个字符串连接;重载运算符”=”,使该运算符实现字符串赋值。编程练习24第六章模板Template25intmax(intx,inty){return(xy)?x:y;}floatmax(floatx,floaty){return(xy)?x:y;}doublemax(doublex,doubley){return(xy)?x:y;}这些函数版本执行的功能都是相同的,只是参数类型和函数返回类型不同。能否为上述这些函数只写出一套代码么?26templateclass类型参数返回类型函数名(模板形参表){//函数体}6.1函数模板的声明与模板函数的生成templateclassTTmax(Tx,Ty){return(xy)?x:y;}voidmain(){inti1=10,i2=45;coutmax(i1,i2)endl;floatf1=12.5,f2=24.5;coutmax(f1,f2)endl;doubled1=12.345,d2=21.3456;coutmax(d1,d2);}函数模板的声明格式如下:27(1)其中,T为类型参数。类型参数前需要加关键字class,这里class不是类的意思,只是借用此关键字class表