您好,欢迎访问三七文档
第八章多态性(polymophism)本章主要内容1.多态的概念2.虚函数——重点3.静态绑定、动态绑定与动态绑定的实现机制4.纯虚函数与抽象类——重点21:36:01§1多态的概念多态性指一个名字,多种语义,或接口相同,多种实现C++支持两种形式的多态1.静态多态(编译时多态)2.动态多态(运行时多态)21:36:01静态多态静态多态主要指函数和运算符的重载例:函数的重载intabs(intx){returnx;}floatabs(floatx){returnx;}longabs(longx){returnx;}voidmain(){coutabs(-100)endl;coutabs(-2.5f)endl;coutabs(123456789L)endl;}21:36:01例:函数的重载(续)classX{public:voidShow(int,char);voidShow(char*,char);};voidmain(){XxObj;xObj.Show(1,'a');xObj.Show(张三,'b');}21:36:01例:函数重载(续)classX{public:voidshow(){}};classY:publicX{public:voidshow(int){}voidshow(){}};voidmain(){XxObj;YyObj;xObj.Show();yObj.Show();yObj.Show(10);yObj.X::Show();}结论:重载函数、运算符重载对应的函数的调用和函数体之间对应关系的建立,是由编译程序和连接程序根据函数的参数数目、参数类型的匹配关系,在编译连接阶段完成的,所以静态多态又称为编译-连接时多态(简称编译时多态)21:36:01动态多态与继承机制有关,建立在继承机制之上在继承机制中,能使用基类对象的地方就能用派生类对象(upcast—向上转换)例:.upcast(向上转换)动态多态在继承机制之上靠虚成员函数实现对虚函数的调用和函数体之间的对应关系不是在编译、连接阶段确定,而是在程序运行过程中动态的确定该函数执行哪一个函数体,因此动态多态又称为运行时多态基类的指针可以指向派生类的对象,基类的引用可以引用派生类的对象。导致编译时无法预知基类指针将来会指向哪个派生类的对象,也无法预知基类的引用将来会引用哪个派生类的对象21:36:01§2虚函数虚函数是在基类中使用关键字virtual进行声明,在派生类中重新定义(override)的成员函数例:虚函数classX{public:virtualvoidf(){coutX::f();}};classY:publicX{public:virtualvoidf(){coutY::f();}};21:36:01虚函数的作用能使用基类对象的地方就能使用派生类对象,故基类指针可以指向派生类对象,基类引用也可以引用派生类对象基类指针指向派生类对象,通过指针调用所指向对象中的虚函数,C++根据所指向对象的类型,在运行时确定调用哪一个类的虚函数基类引用引用派生类对象,通过引用调用所引用对象中的虚函数,C++根据所引用对象的类型,在运行时确定调用哪一个类的虚函数基类指针指向不同派生类的对象,基类引用引用不同派生类的对象时,可以执行虚函数的不同版本21:36:01例1:非虚成员函数classvehicle{public:voiddrive(){coutdriveagenericvehicleendl;}};classcar:publicvehicle{public:voiddrive(){coutdriveacarendl;}};voidf(vehicle&v){v.drive();}voidg(vehicle*pV){pV-drive();}voidmain(){vehiclevh;carc;f(vh);f(c);//分别调用哪一个类中的drive()?g(&vh);g(&c);//分别调用哪一个类中的drive()?vehicle*pVh;pVh=&vh;pVh-drive();pVh=&c;pVh-drive();}引用基类对象,调用基类成员函数引用派生类对象,调用基类成员函数指向基类对象,调用基类成员函数指向派生类对象,调用基类成员函数21:36:01例1:虚成员函数classvehicle{public:virtualvoiddrive(){coutdriveagenericvehicleendl;}};classcar:publicvehicle{public:virtualvoiddrive(){coutdriveacarendl;}};voidf(vehicle&v){v.drive();}voidg(vehicle*pV){pV-drive();}voidmain(){vehiclevh;carc;f(vh);f(c);//分别调用哪一个类中的drive()?g(&vh);g(&c);//分别调用哪一个类中的drive()?}引用基类对象,调用基类成员函数引用派生类对象,调用派生类成员函数指向基类对象,调用基类成员函数指向派生类对象,调用派生类成员函数结论:基类指针指向不同派生类的对象,基类引用引用不同派生类的对象时,执行的是相应派生类中的虚函数21:36:01例2:非虚成员函数classCGraph{intx,y;public:CGraph(intxx,intyy){x=xx;y=yy;}~CGraph(){}voidDraw(CDC&dc){dc.TextOut(x,y,ThisisaGraphObject);}intgetX()const{returnx;}intgetY()const{returny;}};21:36:01例2:非虚成员函数(续)classCRectangle:publicCGraph{intleft,top,right,bottom;public:CRectangle(intlft,inttp,intrgt,intbtm):CGraph((rgt+lft)/2,(tp+btm)/2){left=lft;top=tp;right=rgt;bottom=btm;}~CRectangle(){}voidDraw(CDC&dc){dc.Rectangle(left,top,right,bottom);}};21:36:01例2:非虚成员函数(续)#includeGraph.hclassCCircle:publicCGraph{intr;public:CCircle(intxx,intyy,intrr):CGraph(xx,yy){r=rr;}~CCircle(){}voidDraw(CDC&dc){dc.Ellipse(getX()-r,getY()-r,getX()+r,getY()+r);}};CGraphg(100,30),*pG=&g;CCirclecir(100,100,50);CRectanglerct(100,200,300,300);pG-Draw(dc);//调用哪一个Draw()?pG=○pG-Draw(dc);//调用哪一个Draw()?pG=&rct;pG-Draw(dc);//调用哪一个Draw()?指向CGraph类的对象,调用CGraph类的Draw()指向CCircle类的对象,调用CGraph类的Draw()指向CRectangle类的对象,调用CGraph类的Draw()21:36:01例2:虚成员函数classCGraph{intx,y;public:CGraph(intxx,intyy){x=xx;y=yy;}~CGraph(){}virtualvoidDraw(CDC&dc){dc.TextOut(x,y,ThisisaGraphObject);}intgetX()const{returnx;}intgetY()const{returny;}};21:36:01例2:虚成员函数(续)classCRectangle:publicCGraph{intleft,top,right,bottom;public:CRectangle(intlft,inttp,intrgt,intbtm):CGraph((rgt+lft)/2,(tp+btm)/2){left=lft;top=tp;right=rgt;bottom=btm;}~CRectangle(){}virtualvoidDraw(CDC&dc){dc.Rectangle(left,top,right,bottom);}};21:36:01例2:虚成员函数(续)#includeGraph.hclassCCircle:publicCGraph{intr;public:CCircle(intxx,intyy,intrr):CGraph(xx,yy){r=rr;}~CCircle(){}virtualvoidDraw(CDC&dc){dc.Ellipse(getX()-r,getY()-r,getX()+r,getY()+r);}};CGraphg(100,30),*pG=&g;CCirclecir(100,100,50);CRectanglerct(100,200,300,300);pG-Draw(dc);//调用哪一个Draw()?pG=○pG-Draw(dc);//调用哪一个Draw()?pG=&rct;pG-Draw(dc);//调用哪一个Draw()?指向CGraph类的对象,调用CGraph类的Draw()指向CCircle类的对象,调用CCircle类的Draw()指向CRectangle类的对象,调用CRectangle类的Draw()结论:基类指针指向不同派生类的对象,基类引用引用不同派生类的对象时,执行的是相应派生类中的虚函数21:36:01关于虚函数的说明虚函数只能是类的成员函数,而不能是普通函数或类的友元函数关键字virtual只在声明虚成员函数时使用,定义该成员函数时不需用virtual进行修饰无论继承层次有多深,基类中声明的虚函数,在派生类中自动为虚函数,在派生类中声明该虚函数时可不用关键字virtual21:36:01例:继承层次classRoot{public:virtualvoidvf(){coutBase::vf()endl;}};classFirstLevel:publicRoot{public:voidvf(){coutFirstLevel::vf()endl;}};classSecondLevel:publicFirstLevel{public:voidvf(){coutSecondLevel::vf()endl;}};同一虚函数的不同版本21:36:01例:继承层次(续)voidmain(){Root*ptr;ptr=newRoot;ptr-vf();deleteptr;ptr=newFirstLevel;ptr-vf();deleteptr;ptr=newSecondLevel;ptr-vf();deleteptr;}21:36:01关于虚函数的说明(续)派生类中新添加了与基类中声明的虚函数同名的成员函数,但两者的函数原型不同(overload),则派生类中新添加的成员函数不是基类中同名函数的虚函数classBase{public:virtualvoidf(intx){coutBase::vf(int)endl;}};classDerived:publicBase{public:voidf(){coutDerived::vf()endl;}};派生类中的f()不是基类中虚函数f(int)的新版本派生类中的f()丢失了虚的特性21:36:01关于虚函数的说明(续)继承的类层次中,若某个派生类中没有显式重新定义最上层基
本文标题:C++第八章.
链接地址:https://www.777doc.com/doc-2901798 .html