0第10章多态性与虚函数1.多态性概述2.运算符重载3.虚函数4.纯虚函数与抽象类5.程序设计举例110.1多态性概述多态性面向对象程序设计的关键技术之一。利用多态性技术,可以调用同名函数,实现完全不同的功能分类:(1)编译时的多态性:通过函数的重载和运算符的重载来实现的。(2)运行时的多态性:是指在程序执行前,无法根据函数名和参数来确定该调用哪一个函数,必须在程序执行过程中,根据执行的具体情况来动态地确定。它是通过类继承关系和虚函数来实现的2编译时多态性——函数重载classA{public:……voidf(inta){...}voidf(floatb){...}……};intmain(){At;inta=1;floatb=3.0;t.f(a);t.f(b);...}310.2运算符重载1.重载运算符主要用于对类的对象的操作2.重载运算符使得运算符适用的对象类型得到了拓宽3.重载运算符可以通过创建运算符函数实现,创建operator函数格式如下:类型类名::operator操作符(参数表){……}4.重载的运算符作为类的成员函数,或作为类的友元函数4例10.1定义一个复数类,并重载加法运算符以适应对复数运算的要求。#includeiostreamclassComplex{doublereal,imag;public:Complex(doubler=0,doublei=0):real(r),imag(i){}doubleReal(){returnreal;}doubleImag(){returnimag;}Complexoperator+(Complex&);//重载两个复数相加的+Complexoperator+(double);//重载一个复数加一个实数的+friendComplexoperator=(Complex);//用友元重载赋值运算符=};5ComplexComplex::operator+(Complex&c)//重载两个复数相加运算符+{Complextemp;temp.real=real+c.real;temp.imag=imag+c.imag;returntemp;}6ComplexComplex::operator+(doubled)//重载复数加一个实数运算符+{Complextemp;temp.real=real+d;temp.imag=imag;returntemp;}Complexoperator=(Complexc)//用友元重载赋值运算符={Complextemp;temp.real=c.real;temp.imag=c.imag;returntemp;}7//测试主函数intmain(){Complexc1(3,4),c2(5,6),c3;coutc1=c1.Real()+ic1.Imag()endl;coutc2=c2.Real()+ic2.Imag()endl;c3=c1+c2;//对象(复数)加对象(复数)coutc3=c3.Real()+ic3.Imag()endl;c3=c3+6.5;//对象(复数)加实数coutc3+6.5=c3.Real()+ic3.Imag()endl;return0;}8C++中不允许重载的运算符运算符运算符名称禁止重载的理由?:三目条件运算符C++中没有定义三目运算符的语法.成员操作符为保证成员操作符对成员访问的安全性::作用域操作符该操作符右操作数不是表达式sizeof类型字长操作符该操作符的操作数为类型名,不是表达式*成员指针运算符为保证成员指针符对指向成员引用的安全性9运算符重载的限制不能改变运算符的优先级及结合性;不能改变运算符的操作数个数;不能创建新的运算符,只有已有的运算符可以重载;运算符原有的含义不变;重载的运算符函数不能带有默认参数;注意有些运算符只能重载为成员函数;1010.3虚函数classPet//基类{public:virtualvoidSpeak(){cout“zzz”endl;}//虚函数};classCat:publicPet//派生类{public:virtualvoidSpeak(){coutmiao!miao!endl;}};classDog:publicPet//派生类{public:virtualvoidSpeak(){coutwang!wang!endl;}};例改写前面的例子11intmain(){Petobj,*p1;//基类对象指针p1,基类对象objDogdog1;Catcat1;obj=dog1;//用Dog类对象给Pet类对象赋值obj.Speak();p1=&cat1;//用Cat类对象地址给基类指针赋值p1-Speak();p1=&dog1;//用Dog类对象地址给基类指针赋值p1-Speak();Pet&p4=cat1;//以Cat类对象初始化Pet类引用p4.Speak();return0;}12运行时的多态性举例(类定义同前):voidmain(){inti;Pet*p;Dogdog1;Catcat1;cini;if(i==1)p=&dog1;//根据用户输入确定p的值elsep=&cat1;p-speak();//编译时不能确定}13一、虚函数的定义定义:在某基类中声明为virtual并在一个或多个派生类中被重新定义的成员函数语法:virtual函数返回类型函数名(参数表){函数体}用途:实现多态性,通过指向派生类的基类指针,访问派生类中同名覆盖成员函数(统一接口)注意:在派生类中重新定义虚函数时,其函数名、参数个数、类型及返回值类型都必须相同14二、虚函数的使用限制1.通常只将基类中的public或protected成员声明为虚函数。2.可以通过对象名.、指针或引用调用虚函数;3.基类中声明的虚函数会自动向派生类继承,在派生类中重定义的基类虚函数仍为虚函数,同时可以省略virtual关键字。4.如果派生中没有对基类的虚函数重新定义,则它继承基类中的虚函数。5.不能定义虚构造函数,可以定义虚析构函数6.不能将虚函数声明为静态的或全局的;友元不能作为虚函数15classPet//基类{public:virtualvoidSpeak(){coutzzzendl;}};classCat:publicPet//派生类{public:voidSpeak(){coutmiao!miao!endl;}};classDog:publicPet//派生类{public:voidSpeak(){coutwang!wang!endl;}};voidfun(Pet&i)//普通函数{i.Speak();}例虚函数举例16intmain(){Petobj;Dogdog1;Catcat1;obj=dog1;fun(obj);fun(cat1);fun(dog1);Pet&p4=cat1;fun(p4);return0;}1710.4纯虚函数与抽象类问题:某个类的对象没有意义,如何防止用户创建此类对象?例:classPet{public:virtualvoidSpeak(){}};解决办法:纯虚函数18virtual返回类型函数名(参数表)=0;例:virtualvoidSpeak()=0;注:一般来说,纯虚函数没有实现部分;1.定义格式192.抽象类(1)定义至少包含一个纯虚函数的类称为抽象类(2)使用抽象类的要求抽象类不能实例化抽象类只作为基类被继承可以定义指向抽象类的指针或引用20例抽象宠物类的实现classPet//基类{charName[20];intAge;charColor[12];public:Pet(char*,int,char*);char*GetName(){returnName;}intGetAge(){returnAge;}char*GetColor(){returnColor;}virtualvoidSpeak()=0;//纯虚函数声明virtualvoidGetInfo(){}//虚函数声明};Pet::Pet(char*name,intage,char*color){strcpy(Name,name);Age=age;strcpy(Color,color);}21classCat:publicPet//派生类Cat{public:Cat(char*name,intage,char*color):Pet(name,age,color){}voidSpeak(){cout“miao!miao!”endl;}//具体化纯虚函数voidGetInfo();//重新调整虚函数的功能};voidCat::GetInfo()//重新调整虚函数的功能{coutThecat'sname:GetName()endl;coutThecat'sage:GetAge()endl;coutThecat'scolor:GetColor()endl;}22classDog:publicPet//派生类Dog{public:Dog(char*name,intage,char*color):Pet(name,age,color){}voidSpeak(){coutSoundofspeak:wang!wang!endlendl;}//具体化纯虚函数voidGetInfo();//重新调整虚函数的功能};voidDog::GetInfo()//重新调整虚函数的功能{coutThedog'sname:GetName()endl;coutThedog'sage:GetAge()endl;coutThedog'scolor:GetColor()endl;}23intmain(){Pet*p1;//基类对象指针p1p1=newCat(“MiKey”,1,“Blue”);//动态产生一个Cat类对象p1-GetInfo();p1-Speak();deletep1;//撤消p1所指对象p1=newDog(BenBen,2,Black);//动态产生一个Dog类对象p1-GetInfo();p1-Speak();deletep1;//撤消p1所指对象return0;}24程序设计举例例10.2抽象宠物类的另一种用法#includeiostreamclassPet//宠物基类{public:virtualvoidSpeak()=0;voidShowMe();};voidPet::ShowMe(){cout我的声音:;Speak();}25classCat:publicPet//派生类{public:virtualvoidSpeak(){coutmiao!miao!endl;}};classDog:publicPet//派生类{public:virtualvoidSpeak(){coutwang!wang!endl;}};voidmain(){Catcat1;Dogdog1;cat1.ShowMe();dog1.ShowMe();}26例10.3从Point、Circle类中抽象出基类Shape,研究抽象类和具体类的接口和实现。//shape.h文件定义抽象基类Shape#ifndefSHAPE_H#defineSHAPE_H#includeiostreamclassShape{public:virtualdoubleArea()const{return0.0;}//纯虚函数,在派生类中重载virtualvoidPrintShapeName()const=0;virtualvoidPrint()const=0};#endif27//point.h文件定义类Point#ifndefPOINT_H#definePOINT_H#includesh