c++8多态性.

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

1第八章多态性多态性运算符重载[下半场]虚函数纯虚函数与抽象类深度探索[下半场]2多态性的概念多态性是面向对象程序设计的重要特征之一。多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为。多态的实现:–函数重载–运算符重载–虚函数3问题举例——复数的运算classComplex{//复数类声明public:Complex(doubler=0.0,doublei=0.0){real=r;imag=i;}voiddisplay()const;//显示复数的值private:doublereal;doubleimag;};//classComplex运算符重载4问题举例——复数的运算用“+”、“-”能够实现复数的加减运算吗?实现复数加减运算的方法——重载“+”、“-”运算符运算符重载5运算符重载的实质运算符重载是对已有的运算符赋予多重含义必要性–C++中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类)实现机制–将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参。–编译系统对重载运算符的选择,遵循函数重载的选择原则。运算符重载6运算符重载规则和限制可以重载C++中除下列运算符外的所有运算符:..*::?:只能重载C++语言中已有的运算符,不可臆造新的。不改变原运算符的优先级和结合性。不能改变操作数个数。经重载的运算符,其操作数中至少应该有一个是自定义类型。7两种形式重载为类的非静态成员函数重载为非成员函数运算符重载8运算符函数声明形式函数类型operator运算符(形参){......}重载为类成员函数时参数个数=原操作数个数-1(后置++、--除外)重载为非成员函数时参数个数=原操作数个数,且至少应该有一个自定义类型的形参。运算符重载9运算符成员函数的设计双目运算符B–如果要重载B为类成员函数,使之能够实现表达式oprd1Boprd2,其中oprd1为A类对象,则B应被重载为A类的成员函数,形参类型应该是oprd2所属的类型。–经重载后,表达式oprd1Boprd2相当于oprd1.operatorB(oprd2)运算符重载10运算符重载例8-1[代码]将“+”、“-”运算重载为复数类的成员函数。规则:–实部和虚部分别相加减。操作数:–两个操作数都是复数类的对象。15运算符成员函数的设计前置单目运算符U–如果要重载U为类成员函数,使之能够实现表达式Uoprd,其中oprd为A类对象,则U应被重载为A类的成员函数,无形参。–经重载后,表达式Uoprd相当于oprd.operatorU()运算符重载16运算符成员函数的设计后置单目运算符++和--–如果要重载++或--为类成员函数,使之能够实现表达式oprd++或oprd--,其中oprd为A类对象,则++或--应被重载为A类的成员函数,且具有一个int类型形参。–经重载后,表达式oprd++相当于oprd.operator++(0)运算符重载17例8-2[代码]运算符前置++和后置++重载为时钟类的成员函数。前置单目运算符,重载函数没有形参,对于后置单目运算符,重载函数需要有一个整型形参。操作数是时钟类的对象。实现时间增加1秒钟。运算符重载23运算符非成员函数的设计函数的形参代表依自左至右次序排列的各操作数。后置单目运算符++和--的重载函数,形参列表中要增加一个int,但不必写形参名。如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的友元。运算符重载24运算符非成员函数的设计双目运算符B重载后,表达式oprd1Boprd2等同于operatorB(oprd1,oprd2)前置单目运算符B重载后,表达式Boprd等同于operatorB(oprd)后置单目运算符++和--重载后,表达式oprdB等同于operatorB(oprd,0)运算符重载25例8-3将+、-(双目)重载为非成员函数,并将其声明为复数类的友元,两个操作数都是复数类的常引用。将(双目)重载为非成员函数,并将其声明为复数类的友元,它的左操作数是std::ostream引用,右操作数为复数类的常引用,返回std::ostream引用,用以支持下面形式的输出:coutab;该输出调用的是:operator(operator(cout,a),b);运算符重载#includeiostreamusingnamespacestd;classComplex{//复数类定义public://外部接口Complex(doubler=0.0,doublei=0.0):real(r),imag(i){}//构造函数friendComplexoperator+(constComplex&c1,constComplex&c2);//运算符+重载friendComplexoperator-(constComplex&c1,constComplex&c2);//运算符-重载friendostream&operator(ostream&out,constComplex&c);//运算符重载private://私有数据成员doublereal;//复数实部doubleimag;//复数虚部};//classComplex26Complexoperator+(constComplex&c1,constComplex&c2){returnComplex(c1.real+c2.real,c1.imag+c2.imag);}Complexoperator-(constComplex&c1,constComplex&c2){returnComplex(c1.real-c2.real,c1.imag-c2.imag);}ostream&operator(ostream&out,constComplex&c){out(c.real,c.imag);returnout;}2728静态绑定与动态绑定绑定–程序自身彼此关联的过程,确定程序中的操作调用与执行该操作的代码间的关系。静态绑定[例子代码]–绑定过程出现在编译阶段,用对象名或者类名来限定要调用的函数。动态绑定[例子代码]–绑定过程工作在程序运行时执行,在程序运行时才确定将要调用的函数。虚函数33虚函数虚函数是动态绑定的基础。是非静态的成员函数。在类的声明中,在函数原型之前写virtual。virtual只用来说明类声明中的原型,不能用在函数实现时。具有继承性,基类中声明了虚函数,派生类中无论是否说明,相同原型函数都自动为虚函数。本质:不是重载声明而是覆盖。调用方式:通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数。虚函数34例8-4#includeiostreamusingnamespacestd;classBase1{//基类Base1定义public:virtualvoiddisplay()const;//虚函数};//endclassBase1voidBase1::display()const{coutBase1::display()endl;}//Base1::displayclassBase2:publicBase1{//公有派生类Base2定义public:virtualvoiddisplay()const;//覆盖基类的虚函数};//endclassBase2voidBase2::display()const{coutBase2::display()endl;}//Base2::display虚函数//公有派生类Derived定义classDerived:publicBase2{public:virtualvoiddisplay()const;//覆盖基类的虚函数};//endclassDerivedvoidDerived::display()const{coutDerived::display()endl;}//Derived::display//参数为指向基类对象的指针voidfun(Base1*ptr){ptr-display();//对象指针-成员名}//fun35voidmain(void){//主函数Base1base1;//定义Base1类对象Base2base2;//定义Base2类对象Derivedderived;//定义Derived类对象fun(&base1);//用Base1对象的指针调用fun函数fun(&base2);//用Base2对象的指针调用fun函数fun(&derived);//用Derived对象的指针调用fun函数}//main运行结果:Base1::display()Base2::display()Derived::display()3637虚析构函数为什么需要虚析构函数?可能通过基类指针删除派生类对象;如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),就需要让基类的析构函数成为虚函数,否则执行delete的结果是不确定的。例见8_5.cpp虚函数38抽象类带有纯虚函数的类称为抽象类:class类名{virtual类型函数名(参数表)=0;//纯虚函数...}纯虚函数与抽象类39抽象类纯虚函数与抽象类作用–抽象类为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。–对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。注意–抽象类只能作为基类来使用。–不能声明抽象类的对象。–构造函数不能是(纯)虚函数,析构函数可以是。40例8-6纯虚函数与抽象类classBase1{//基类Base1定义public:virtualvoiddisplay()const=0;//纯虚函数};//endclassBase1classBase2:publicBase1{//公有派生类Base2定义public:voiddisplay()const{//覆盖基类的虚函数coutBase2::display()endl;}//display};//endclassBase2classDerived:publicBase2{//公有派生类Derived定义public:voiddisplay()const{//覆盖基类的虚函数coutDerived::display()endl;}//display};//endclassDerivedvoidfun(Base1*ptr){ptr-display();//对象指针-成员名}//funvoidmain(void){//主函数Base2base2;//定义Base2类对象Derivedderived;//定义Derived类对象fun(&base2);//用Base2对象的指针调用fun函数fun(&derived);//用Derived对象的指针调用fun函数}//main运行结果:Base2::display()Derived::display()41多态类型与非多态类型多态类型与非多态类型–有虚函数的类类型称为多态类型–其它类型皆为非多态类型二者的差异–语言层面的差异多态类型支持运行时类型识别多态类型对象占用额外的空间–设计原则上的差异42深度探索设计原则多态类型–多态类型的析构函数一般应为虚函数非多态类型–非多态类型不宜作为公共基类由于没有利用动态多态性,一般可以用组合,而无需用共有继承;如果继承,则由于析构函数不是虚函数,删除对象时所执行操作与指针类型有关,易引起混乱。–把不需被继承的类型设定为非多态类型由于成员函数都是静态绑定,调用速度较快;对象占用空间较小。43深度探索运行时类型识别运行时类型识别–允许在运行时通过基类指针(或引用)辨别对象所属的具体派生类;–只对多态类型适用

1 / 40
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功