您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > W07-01-清华大学面向对象程序设计虚函数与多态_680204193
面向对象程序设计清华大学计算机科学与技术系徐明星、黄震春2015@6A0182这个信息不是我们想要的答非所问的输出!Instrument::play#includeiostreamusingnamespacestd;enumnote{middleC,Csharp,Eflat};classInstrument{public:voidplay(note)const{coutInstrument::playendl;}};//WindobjectsareInstruments//becausetheyhavethesameinterface:classWind:publicInstrument{public://Redefineinterfacefunction:voidplay(note)const{coutWind::playendl;}};voidtune(Instrument&i){//...i.play(middleC);}intmain(){Windflute;tune(flute);//Upcasting}3原因是:函数调用是在编译期间确定的•编译器只知道传给tune的类型是instrument,所以它只会安排调用基类函数•希望能根据对象的实际类型来调用正确函数!•怎么改程序呢?为什么会这样?4解决之道:虚函数!#includeiostreamusingnamespacestd;enumnote{middleC,Csharp,Eflat};//Etc.classInstrument{public:virtualvoidplay(note)const{coutInstrument::playendl;}};程序输出Wind::playvirtual关键字说明函数是虚函数只要某个函数在基类中声明为虚函数,则在派生类中也是虚函数,而不论是否在派生类中也这样声明多态(Polymorphism)5按照基类的接口定义,调用指针或引用所指对象的接口函数,函数执行过程因对象的不同而呈现不同的效果(表现),这个现象被称为“多态”。多态,使得C++语言可以用一段相同的代码,在运行时完成不同的任务,这些不同运行结果的差异由派生类之间的差异决定。6产生多态效果的条件——继承&&虚函数&&(引用或指针)#includeiostreamusingnamespacestd;enumnote{middleC,Csharp,Eflat};//Etc.classInstrument{public:virtualvoidplay(note)const{cout“Instrument::play”endl;}};classWind:publicInstrument{public:voidplay(note)const{coutWind::playendl;}};voidtune(Instrument*p){//...p-play(middleC);}intmain(){Windflute;tune(&flute);}思考题:下面代码的输出?11cout=====================endl;coutcallA[i]-what()....endl;for(inti=0;i4;i++){cout'['i]:A[i]-what()endl;}14纯虚函数——“抽象类”、“接口”•前例中,instrument类没有特定事物与之对应,不能用于表示特例的实现•它在设计上的意义(作用)是:对派生类规定公共“接口”。所以定义它的对象是没有意义的•使用C++中的纯虚函数,可以使得“这种类是抽象的,仅表示接口”这个意思表达得更直接、更清楚,即:•总结设计要点:含有纯虚函数的类是抽象类,不能定义对象,它在设计上所起到作用是“规定类的继承体系的公共接口”!virtualvoidx()=0;18什么时候使用继承及虚函数?•希望“同一段代码实际处理的是不同类型的对象,但效果却完全相同”,则应:(1)使用继承,不同类型对象都是基类派生类的对象&&(2)代码统一按基类对象处理•希望“同一段代码实际处理的是不同类型的对象,运行的实际效果(结果)也有差异”,则应:(1)使用继承,不同类型对象都是基类派生类的对象&&(2)有差异部分通过调用虚函数来实现&&(3)代码统一按指针或引用形式处理不同对象19STEP1STEP2STEP3STEP4STEP5function(...)classBase{public:voidfunction(...);//...};虚函数的典型应用场景举例设要求对象具有如下功能(接口)function,完成该功能需要5个步骤。通常,对上述设计需求,可以先按如下方式定义一个类。20虚函数的典型应用场景举例根据程序的实际应用情况,有时需要变更或增强程序的一些设计。例如:希望新增一类对象,它在概念上属于Base类,但接口function的内容(步骤)需要调整和改变。如右图所示:function接口的STEP2、STEP4步需要变化,而其他步骤则维持不变。21虚函数的典型应用场景举例常见做法是:以Base类为基类,定义一个新的派生类,在派生类中重定义function接口函数,即function的具体内容发生了变化。——但是,这样一种重新定义基类已定义接口函数的设计方法,在设计思想的一致性、连贯性上存在问题:1.既然一个接口函数在基类中定义,则表明设计者的意图是:这个接口是所有基类对象、派生类对象所共有、共享的共同接口(的规范)。所以派生类均应遵守,不应变动。2.而在某个派生类中对某个已有接口函数(从基类继承而来,所以说它是已有的)进行重新定义,表明设计者认为:派生类对于该接口的内涵定义需求与基类不同,不能遵守基类设置的规范。3.上述事实表明:这种“不守规矩”的派生类,不能因为想借用基类已定义其他部分就从基类派生,而应该另外定义无继承关系的独立类。22虚函数的典型应用场景举例如果从概念上,确实存在有继承关系,只是接口function的内容确实也需要改变,则正确的做法应该是:在Base类中将function定义为虚函数,在派生类中给出function接口函数更有意义的特殊定义。classBase{public:virtualvoidfunction(...);//...};classDerived:publicBase{public:virtualvoidfunction(...);//...};23虚函数的典型应用场景举例对于某个继承体系中不同类型对象的同一接口(如前述示例中的接口函数function),在实际使用时,通常将相关代码段抽象成一个函数(普通函数或其他某类的成员函数),以继承体系根节点处的基类指针或引用为函数参数。代码示意如下:voiduse_it(Base*pobj){////otherstatements...pobj-function(...);////otherstatements...}//...Base*pobj=newDerived();use_it(pobj);24虚函数的典型应用场景举例更进一步,设接口函数function有多种不同算法实现,这些不同的设计实现之间相同的步骤与不同的步骤相互交错(如下图所示)。如果希望充分发挥继承在代码复用上的优势,同时明确表达不同算法之间的区别与联系,则可以用多个不同成员函数来实现function中的各个步骤,并将其中有变化(即算法不同)的步骤设置为虚函数(甚至纯虚函数),在派生类中给出合适的定义。25虚函数的典型应用场景举例classBase{public:voidfunction(...);virtualvoidstep1();virtualvoidstep2();virtualvoidstep3();virtualvoidstep4();voidstep5();};classDerived_1:publicBase{public:virtualvoidstep2();virtualvoidstep4();};classDerived_2:publicBase{public:virtualvoidstep1();virtualvoidstep3();virtualvoidstep4();};TEMPLATEMETHOD设计模式•在基类中定义算法的框架,其中使用(调用)了一些抽象操作。•在派生类中,根据需要定义上述抽象操作的具体内容,即通过重定义来提供操作的具体行为。•模板方法是一种代码利用的基本技术,在类库的设计实现中应用十分广泛——因为这个设计模式能有效地解决“类库提供公共行为”与“用户定制特殊细节”之间的折衷平衡。26示例代码示例代码示例代码示例代码示例代码运行结果结束
本文标题:W07-01-清华大学面向对象程序设计虚函数与多态_680204193
链接地址:https://www.777doc.com/doc-2866857 .html