第9讲多态性与虚函数_第1页
第9讲多态性与虚函数_第2页
第9讲多态性与虚函数_第3页
第9讲多态性与虚函数_第4页
第9讲多态性与虚函数_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

9.1多态性概述9.2运算符重载

9.2.1重载为成员函数

9.2.2重载为友元函数

第9讲多态性与虚函数9.3虚函数9.4纯虚函数与抽象类9.5编程实例—日期类多态性是指不同的对象接受到相同的消息时产生不同的响应动作,例如对应相同的函数名,却执行了不同的函数体。

9.1多态性概述多态性与前面介绍的继承、封装等一样,都是面向对象程序设计语言中的重要特征。一般来说,C++语言支持两种不同类型的多态:编译时多态和运行时多态。编译时多态主要通过函数重载和运算符重载实现,运行时多态主要通过虚函数来实现。

运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据导致不同类型的行为。其主要优点在于用户自定义的数据类型可使用编译系统预定义的运算符。定义运算符重载函数的函数名字必须以operator开头。9.2运算符重载

运算符重载是通过函数来实现的,而且运算符重载函数应能访问类的私有成员,所以,运算符重载函数有两种形式:成员函数友元函数#include<iostream.h>classComplex

//复数类声明{private:

doublerealpart,imagpart;public:Complex(){realpart=0.0;imagpart=0.0;}//构造函数

Complex(doublec){realpart=c;imagpart=0;}Complex(doubler1,doublei1){realpart=r1;imagpart=i1;}doublereal(){returnrealpart;}

//取复数的实部和虚部

doubleimag(){returnimagpart;}

Complexoperator+(Complexc)

//算术运算符重载为成员函数

{returnComplex(realpart+c.realpart,imagpart+c.imagpart);}voidDisplay(){cout<<real()<<"+"<<imag()<<"i";}//显示复数};voidmain(){Complexx1(2.0),x2(3.0,-7.0),x3;

x3=x1+x2;x3.Display();}案例9.1复数类运算符重载。程序执行结果:5+-7i

将“+”运算重载为复数类的成员函数。规则:实部和虚部分别相加。操作数:两个操作数都是复数类的对象。运算符重载函数的一般形式为:

类型类名::operator重载的运算符(参数列表){

相关操作;}重载为类成员函数时

参数个数=原操作数个数-1(后置++、--除外)重载为友元函数时参数个数=原操作数个数,且至少应该有一个自定义类型的形参。(1)虽然运算符重载可以改变运算符原来的行为,但是它并不能改变运算符的优先级、结合性、操作数的个数。也不能创建新的运算符,只能重载现有的运算符。运算符重载具有非常强的灵活性。在应用时要注意以下几点问题:(3)在重载运算符()、[]、->或者=时,运算符重载函数必须声明为类的成员函数。对于其他的运算符,运算符重载函数可以是成员函数或者友元函数。(2)运算符重载函数的参数至少有一个必须是自定义类型。运算符可重载

+

-

*

/ %

^ & | ~ !

= < > +=-=

*= /= %= ^= &=

|= << >> >>= <<=

== != <= >= &&

|| ++ -- ->,

->

[] () new delete

.

*

::

?:

sizeof不可重载表9.1可以重载和不可重载的运算符9.2.1运算符重载为成员函数案例6.2classClock //时钟类声明{public:Clock(intNewH=0,intNewM=0,intNewS=0)//构造函数{Hour=NewH;Minute=NewM;Second=NewS;}voidShowTime(){cout<<Hour<<“时”<<Minute<<“分”<<Second<<“秒\n";}voidoperator++();

//前置单目运算符重载

voidoperator++(int);

//后置单目运算符重载

private:

intHour,Minute,Second;};voidClock::operator++()//前置单目运算符重载

{Second++;if(Second>=60){Second=Second-60;Minute++;if(Minute>=60){Minute=Minute-60;Hour++;Hour=Hour%24;}}cout<<"++Clock:";}运算符前置++和后置++重载为时钟类的成员函数:操作数是时钟类的对象。实现时间增加1秒。voidClock::operator++(int)

//后置单目运算符重载{Second++;if(Second>=60){ Second=Second-60;Minute++; if(Minute>=60){Minute=Minute-60;Hour++;Hour=Hour%24;}}cout<<"Clock++:";}voidmain(){ClockmyClock(23,59,59);cout<<"Firsttimeoutput:";myClock.ShowTime();myClock++;myClock.ShowTime();++myClock;myClock.ShowTime();

}运算符成员函数的设计后置单目运算符++和--如果要重载++或--为类成员函数,使之能够实现表达式

oprd++或oprd--,其中

oprd若为A类的对象,则++或--应被重载为A类的成员函数,且具有一个int类型形参。经重载后,表达式

oprd++相当于oprd.operator++(0)前置单目运算符,重载函数没有形参。9.2.2运算符重载为友元函数案例9.3复数类对象的算术运算。#include<iostream.h>classcompl

//复数类声明{public:

compl(doubler=0.0,doublei=0.0){real=r;imag=i;}//构造函数

friendcomploperator+(complc1,complc2);//+重载为友元函数

friendcomploperator-(complc1,complc2);//-重载为友元函数

voiddisplay() //显示复数的值{cout<<"("<<real<<","<<imag<<")"<<endl;}private: doublereal,imag;}; comploperator+(complc1,complc2)//运算符重载友元函数实现{returncompl(c2.real+c1.real,c2.imag+c1.imag);}Comploperator-(complc1,complc2)//运算符重载友元函数实现{returncompl(c1.real-c2.real,c1.imag-c2.imag);}voidmain()

//主函数{complexc1(5,4),c2(2,10),c3;//声明复数类的对象

c3=c1-c2;

//使用重载运算符完成复数减法

cout<<"c3=c1-c2=";c3.display();

c3=c1+c2;

//使用重载运算符完成复数加法

cout<<"c3=c1+c2=";c3.display();}续#include<iostream.h>classRect{public:Rect(inta,intb){length=a;width=b;} //构造函数

friendintoperator>(Rect&r1,Rect&r2)//运算符>重载为友元函数{return(r1.length*r1.width>r2.length*r2.width?1:0);}voiddisplay(){cout<<length*width<<endl;}private: intlength,width;};voidmain()

//主函数{Rects1(5,4),s2(3,8);

//声明复数类的对象

cout<<“长方形面积1:";

s1.display();cout<<“长方形面积2:";

s2.display();if(s1>s2)cout<<“s1面积大于s2面积.”<<endl;

elsecout<<“s1面积小于或等于s2面积."<<endl;}使用重载运算符完成对象的比较案例9.4关系运算符重载为友元函数。它可以改变C++语言所提供的大多数运算符的含义。它必须是成员函数或者友元函数。重载运算符允许使用一种非常自然的表达方式来表达用户自定义类型的数据之间的关系。运算符重载不能改变运算符的优先级和结合性,不能改变运算符操作数的个数,也不能增加新的运算符。重载运算符时,要使运算符的含义与数学中的规定和人们的常识相符,并且对函数的功能给予必要的说明。否则就有可能将运算符的含义隐藏在代码中,使人难以理解。注运算符重载具有许多优点:9.3虚函数虚函数是动态联编(运行时多态)的基础。是非静态的成员函数。在类的声明中,在函数原型之前写virtual。virtual

只用来说明类声明中的原型,不能用在函数实现时。具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。本质:不是重载声明而是覆盖。调用方式:通过基类指针或引用,执行时会

根据指针指向的对象的类,决定调用哪个函数。#include<iostream.h>classMammal

//类的声明及其实现{public:Mammal(){cout<<"Mammalconstructorcalled."<<endl;}virtual~Mammal()//用virtual声明虚函数{cout<<"Mammaldestructorcalled."<<endl;}//虚函数在基类中的实现

virtualvoidSpeak(){cout<<"Mammalspeak."<<endl;}};classDog:publicMammal//公有派生类Mammal{public:Dog(){cout<<"Dogconstructorcalled."<<endl;}~Dog(){cout<<"Dogdestructorcalled."<<endl;}voidSpeak(){cout<<"Woof!"<<endl;}//派生类中虚函数的实现};voidmain(){Mammal*p=newDog;p->Speak();

deletep;}案例9.5虚函数的应用。声明虚成员函数的格式:

virtual

函数类型函数名称(形式参数表);虚函数的声明只能出现在类声明时的函数原型声明中。在派生类中可以不显示地声明为虚函数,系统会自动判别该函数是虚函数或者重载函数。注说明:声明了基类指针,就可以使不同的派生类对象调用不同的函数,实现了程序的运行时多态。运行时多态应该使用虚函数,并通过指针、引用或者成员函数调用虚函数。#include<iostream.h>classFruit//水果类的声明及其实现{public:Fruit(){}

virtualvoiddispFruitName()//用virtual声明虚函数dispFruitName(){cout<<"It'sfruitclass!"<<endl;}//虚函数在基类中的实现

virtualvoidtaste(){cout<<"Cannoteatanabstractfruit!"<<endl;}};classorange:publicFruit//公有派生类Orange{private:

charorangeName[20];public:orange(){}voidsetorangeName(charorangeN[20]){strcpy(orangeName,orangeN);}voiddisporangeName(){cout<<"Theorangenameis"<<orangeName<<endl;}voiddispFruitName(){cout<<"Thefruitnameisorange."<<endl;}//派生类中虚函数的实现

voidtaste(){cout<<“It‘ssweet!”<<endl;}//派生类中虚函数的实现};

案例9.6水果类voidmain(){Fruitmyfruit,*ptrfruit;//声明水果类的对象及指针

orangemyorange,*ptrorange;//声明橘子类的对象及指针

myorange.setorangeName(“Huangyan”);

ptrfruit=&myfruit;ptrorange=&myorange;ptrfruit->dispFruitName();ptrfruit->taste();ptrorange->dispFruitName();ptrorange->disporangeName();ptrorange->taste();ptrfruit=ptrorange;

//将基类指针指向派生类对象

ptrfruit->dispFruitName();

//访问派生类的成员函数

ptrfruit->taste();}

(续)

9.4抽象类抽象类为抽象和设计的目的而建立,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。抽象类只能作为基类来使用。不能声明抽象类的对象。构造函数不能是虚函数,析构函数可以是虚函数。一旦在基类中声明了纯虚函数,该基类就成为抽象类,若该类的某个派生类没有给出基类中的纯虚函数的全部实现,则该派生类依然是抽象类。(带有纯虚函数的类称为抽象类)class类名

{

virtual类型函数名(参数表)=0;//纯虚函数...}抽象类的定义形式:#include<iostream.h>classB0//抽象基类B0声明{public:virtualvoiddisplay()=0;//纯虚函数成员};classB1:publicB0 //公有派生{public:voiddisplay(){cout<<"B1::display()"<<endl;}//虚成员函数};classD1:publicB1 //公有派生{public:voiddisplay(){cout<<"D1::display()"<<endl;}//虚成员函数};voidfun(B0*ptr){ptr->display();}//普通函数voidmain(){B0*p;//声明抽象基类指针

B1b1;//声明派生类对象

D1d1;//声明派生类对象

p=&b1;fun(p);//调用派生类B1函数成员

p=&d1;fun(p);//调用派生类D1函数成员}案例9.7抽象类与纯虚函数.可以声明指向抽象类的指针,虽然该指针不能指向任何抽象类的对象(因为不存在),但可以通过该指针获得对派生类成员函数的调用。案例9.8求不同类型图形的面积#include<iostream.h>classShape

//声明抽象类作为基类{protected:doublex,y;public:Shape(){}

//构造函数

Shape(doublexx,doubleyy);~Shape(){}//析构函数

virtualvoiddispName()=0;//声明纯虚函数

virtualvoiddispArea()=0;//声明纯虚函数};classCircle:publicShape

//声明派生类Circle(圆){private:doubleradius,area;public:Circle(doublecx=1,doublecy=1,doublerad=1);//构造函数~Circle(){}

//析构函数

voiddispName(){cout<<"Currentshape'snameisCircle!";}voiddispArea(){area=3.14*radius*radius;cout<<"Theareaofthecircleis:"<<area<<endl;}};说明:在基类中声明2个虚函数,dispName(),dispArea()。这些虚函数在其派生类Circle和Rectangle中获得不同的实现。classRectangle:publicShape//声明派生类Rectangle(长方形){private:doublelength,width,area;public:Rectangle(doublecx=1,doublecy=1,doublelen=2,doublewid=1);~Rectangle(){}//析构函数

voiddispName(){cout<<"Currentshape'snameisRectangle!";}voiddispArea(){area=length*width;cout<<"Theareaoftherectangleis:"<<area<<endl;}};Shape::Shape(doublexx,doubleyy):x(xx),y(yy){}//构造函数Circle::Circle(doublecx,doublecy,doublerad)//构造函数

:Shape(cx,cy),radius(radi){}Rectangle::Rectangle(doublecx,doublecy,doublelen,doublewid):Shape(cx,cy),length(len),width(wid){}//构造函数(续)voidmain(){Shape*ptrshape;

//声明抽象类指针

Circlecircle1(2,3,3);

//声明派生类Circle的对象

Rectanglerect1(4,5,8,3);//声明派生类Rectangle的对象

circle1.dispName();circle1.dispArea();ptrshape=&circle1;//抽象类指针指向派生类Circle的对象

ptrshape->dispName();ptrshape->dispArea();rect1.dispName();rect1.dispArea();ptrshape=&rect1;//抽象类指针指向派生类

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论