类的继承与多态性_第1页
类的继承与多态性_第2页
类的继承与多态性_第3页
类的继承与多态性_第4页
类的继承与多态性_第5页
已阅读5页,还剩49页未读 继续免费阅读

下载本文档

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

文档简介

类的继承与多态性一、继承得概念继承就是软件重用得一种方式。通过继承建立新类得优越性:通过重用已有代码,提高编程效率,降低软件开发成本;更有效地保持共有特性得一致性;提高了系统得可维护性;就是实现多态性得基础。被继承得类称为“父类”或“基类”;继承者被称为“子类”或“派生类”;§9、1类得继承继承体现了类得“层次”,从层次上来说,就就是位于上层得就是基类,下层得为派生类;派生类吸收了基类得非private成员,并增添新得性能。基类、派生类就是相对得。基类与派生类示例基类派生类学生本科生,研究生形状圆,三角形,矩形,球体,立方体贷款汽车贷款,住房贷款,抵押贷款雇员教职员工,后勤人员账户支票账户,储蓄账户一般地,派生类得对象都“就是”其基类得一个对象;基类所能表示得对象集合比派生类所表示得对象集合大、eg、矩形类“就是”四边形类,反之不成立、单继承建筑物房屋桥梁纪念塔平房楼房居民楼办公楼单继承:派生类仅由一个基类派生而来多继承EmployeeFacultyStaffAdministratorTeacherAdministratorteacher多重继承:派生类由多于一个得基类派生而成多级继承:等于或多于两个得继承层次二、派生类得定义定义格式:

class<派生类名>:<继承方式><基类名>{派生类新定义成员};继承方式包括:

public/protected/private

分别表示派生类公有继承基类,

派生类保护继承基类,

派生类私有继承基类、classA{pulbic:A(intaa=0){a=aa;}voidshowA();private:inta;};classB:publicA{public:B(intbb=0){b=bb;}private:intb;};聚合VS继承聚合:B类对象得成员包括A类得一个或多个对象。继承:B类对象得成员包括A类得成员。classA{pulbic:A(intaa=0){a=aa;}voidshowA();private:inta;};classB:publicA{public:B(intbb=0){b=bb;}private:intb;};classA{pulbic:A(intaa=0){a=aa;}voidshowA();private:inta;};classB{public:B(intbb=0):aa(0){b=bb;}private:intb;Aaa;};B-b:int+showA(),+B()B-b:int,-aa:A+B()派生类成员得构成后举例基类中得私有成员不能被派生类成员直接引用。派生类对象先存储其基类子对象部分,再存储新增部分。派生类成员和外部对象访问基类中得私有成员派生类专属成员使用基类私有成员得方式:①不能直接使用基类中得私有成员;②成员函数方式:派生类成员函数需要通过基类提供得公有或保护成员函数间接访问;外部对象通过基类得公有成员函数使用其私有成员;③友元方式:把派生类定义为基类得友元,则派生类成员可以直接使用基类成员;而派生类对象则可以用友元函数间接使用基类私有成员、派生类得三种继承方式公有继承:除基类得私有成员继续为基类得私有成员外,基类得公有成员和保护成员分别成为派生类得公有成员和保护成员。私有继承:除基类得私有成员继续为基类得私有成员外,基类得公有成员和保护成员将成为派生类得私有成员。保护继承:除基类得私有成员继续为基类得私有成员外,基类得公有成员和保护成员将成为派生类得保护成员。大家有疑问的,可以询问和交流可以互相讨论下,但要小声点继承方式基类成员public方式派生类内/类外对象private方式派生类内/类外对象protected方式派生类内/类外对象private不可见不可见不可见protected可见/不可见可见/不可见可见/不可见public可见可见/不可见可见/不可见三种继承方式存取权限得关系类内:指派生类内得成员函数类外:指类外部定义得派生类对象可见:即可以直接访问(使用)不可见:只能间接访问继承成员得访问属性---举例示例

classX{private:inta;protected:intb;public:intc;};classY1:publicX{intd;public:

output(){returnd;}};Y1得构成-----

私有:d

保护:b

公有:output(),c不可直接访问:a(仍属于基类)继承成员得访问属性---举例示例classX{private:inta;protected:intb;public:intc;};classY2:protectedX{intd;public:

output(){returnd;}};Y2得构成----

私有:d

保护:bc

公有:output()

不可直接访问:a(仍属于基类)继承成员得访问属性---举例示例classX{private:inta;protected:intb;public:intc;};classY3:private

X{intd;public:

output(){returnd;}};Y3得构成----

私有:dbc

保护:公有:output()

不可直接访问:a(仍属于基类)(1)public继承方式在public继承方式下,派生类内得成员函数中可以访问基类和派生类中得保护成员,但外部程序中定义得派生类对象不可访问基类和派生类中得保护成员。Eg、例题“public继承”在public继承方式下,派生类内得成员函数中可以访问基类和派生类中得保护成员,但外部程序中定义得派生类对象不可访问基类和派生类中得保护成员。Eg、例题“private继承”(2)private继承方式Eg、例题“public继承”#include<isotream、h>classA{private:inta1;protected:inta2;public:inta3;A(intx1,intx2,intx3):A(x1),a2(x2),a3(x3){}~A(){}};classB:publicA{private:intb1;protected:intb2;public:intb3;B(intx1,intx2,intx3,inty1,inty2,inty3):A(x1,x2,x3),b1(y1),b2(y2),b3(y3){}~B(){}voidprint()const;};voidB::print()const{//cout<<“a1=”<<a1<<endl;//非法

cout<<“a2=”<<a2<<endl;cout<<“a3=”<<a3<<endl;cout<<“b1=”<<b1<<endl;cout<<“b2=”<<b2<<endl;cout<<“b3=”<<b3<<endl;}voidshow(B&myB){//cout<<myB、a1<<myB、a2<<endl;//非法

cout<<“a3=”<<myB、a3<<endl;//cout<<myB、b1<<myB、b2<<endl;//非法

cout<<“b3=”<<myB、b3<<endl;Eg、例题“private继承”#include<isotream、h>classA{private:inta1;protected:inta2;public:inta3;A(intx1,intx2,intx3):A(x1),a2(x2),a3(x3){}~A(){}};classB:privateA{private:intb1;protected:intb2;public:intb3;B(intx1,intx2,intx3,inty1,inty2,inty3):A(x1,x2,x3),b1(y1),b2(y2),b3(y3){}~B(){}voidprint();};voidB::print(){//cout<<“a1=”<<a1<<endl;//非法

cout<<“a2=”<<a2<<“a3=”<<a3<<endl;cout<<“b1=”<<b1<<“b2=”<<b2<<endl;cout<<“b3=”<<b3<<endl;}voidshow(AmyA,B&myB){//cout<<myA、a1<<myA、a2;非法

cout<<myA、a3<<endl;//cout<<“myB、a1<<myB、a2<<endl;//非法

//cout<<“a3=”<<myB、a3<<endl;//非法

//cout<<myB、b1<<myB、b2<<endl;//非法

cout<<“b3=”<<myB、b3<<endl;三、派生类得构造函数继承关系中,基类得两个特殊成员函数---构造函数和析构函数不能被继承。因此,派生类得构造函数不仅要初始化派生类中定义得数据成员,而且要初始化派生类中得基类子对象(派生类对象中由基类定义得部分称作基类子对象)。基类子对象得初始化赋值就是通过在派生类构造函数中显式或隐式地调用基类构造函数实现得。构造函数得执行顺序:先执行基类构造函数,再执行派生类构造函数。基类子对象派生类专有部分派生类例1:P267例题classA

{

inta1;

protected:

inta2;

public:

inta3;

A(){a1=a2=a3=0;}

A(intx1,intx2,intx3):a1(x1),a2(x2),a3(x3){}

voidout(){cout<<a1<<‘‘<<a2<<‘‘<<a3<<endl;}

intGeta1(){returna1;}

};返回B类classB:publicA{

intb1;

protected:

intb2;

public:

intb3;

B(){b1=b2=b3=0;}

B(intx1,intx2,intx3):A(x1,x2,x3)

{b1=x1+1;b2=x2+2;b3=x3+3;}

voidout(){A::out();

cout<<b1<<‘‘<<b2<<‘‘<<b3<<endl;}

intSum(){returnGeta1()*b1+a2*b2+a3*b3;}};隐含调用A()显式调用

A(int,int,int)调用类A中得无参构造函数返回main()函数//A类和B类中都有Out(),则用::界定其所属,若不加则指得就是本类成员、调用类A中得带参构造函数

voidmain(){

Bb1,b2(1,2,3);b1、Out();b2、Out();cout<<b1、Sum()<<‘‘<<b2、Sum()<<endl;cout<<sizeof(B)<<endl;}调用类B中得构造函数初始化对象四、派生类得析构函数撤销派生类对象时,系统先自动调用派生类得析构函数,再调用基类得析构函数。系统对派生类和基类析构函数得自动调用顺序和其对构造函数得调用顺序正好相反。例:类层次中普通析构函数五、基类和派生类得赋值兼容规则在满足公有继承得限制条件下,C++允许派生类对象到基类对象得自动转换,通常称为赋值兼容规则。规则1:可以用派生类对象为基类对象赋值规则2:可以用派生类对象初始化基类得引用对象规则3:可以用基类得指针指向派生类对象eg、赋值兼容规则eg、类层次中普通析构函数§9、2多重继承多级继承引例:Windows风格用户界面设计先设计出窗口类、尺寸类、横向/纵向滚动条类、按钮类;用户界面类将上述各类作为基类,通过多重继承产生。一、多重继承时得派生类

1、定义格式:

class派生类名:

[继承方式1][基类名1],[继承方式2][基类名2],…{派生类成员定义};注:各基类名不能相同2、初始化数据成员—调用构造函数1)调用顺序构造函数调用顺序:

(1)初始化第一个基类子对象,即调用第一个基类得构造函数;初始化第二个基类子对象,即调用第二个基类得构造函数;

……

初始化第n个基类子对象,即调用第n个基类得构造函数。

(2)派生类中若有基类对象作成员,则调用相应基类得构造函数。

(3)最后初始化派生类普通成员。析构函数调用顺序:与构造函数相反。例:多重继承时得构造函数得调用P269说明:派生类初始化成员得顺序:

首先对基类子对象成员得初始化,

然后对新增对象成员初始化,

最后对新增非对象成员初始化、注:前两步必须在构造函数得初值表中给出初值项

(省略时调用无参构造函数);

后一步可以通过初值表,也可以通过函数体进行、二、多级继承中得派生类

classA{};classB:publicA{};classC:publicB{};B直接继承类A,C直接继承类B,间接继承类A。eg、P270多级继承举例多级继承时得初始化顺序:先执行最上层基类得构造函数,依次向下执行各基类得构造函数,最后就是当前派生类得构造函数对新增成员得初始化、执行C得构造函数前先调用B1、B2构造函数;执行B1、B2构造函数前先分别调用A得构造函数。AB1B2CA为C得公共基类eg、数据成员二义性三、混合多继承中得构造函数classC:publicB1,publicB2{};1、数据成员二义性假设图中类A有数据成员a,则C中有两个a成员,a就是哪个派生类得成员?产生了二义性。解决方法:

(1)加类名分辨符B1::或B2::(2)定义虚基类

eg1、数据成员二义性A(a)B1(a,b1)B2(a,b2)C(a,b1,b2,c)四、多继承中二义性问题得解决2、函数成员二义性

B1,B2中都含有print()成员函数,C继承了B1,B2,则C中有两个print()函数。解决方法:加类区分符::eg2、函数成员二义性B1(print())B2(print())C§9、3虚基类消除数据成员二义性得两种方法得区别:

(1)”类名::”法:消除二义性同时实现程序设计得多态性,但在派生类对象中存在多个公共基类得同名数据成员

(2)”虚函数”法:消除二义性,且在派生类对象中只存在一个公共基类得数据成员设计虚基类:

(1)一个基类有多于一个派生类时,在这些派生类“继承方式”前加上

virtual关键字。

classB1:virtualpublicAclassB2:virtualpublicA

此时将公共基类A称为虚基类。AB1B2C(2)虚基类子对象由最新派生出来得派生类直接初始化。

B1,B2对类A子对象初始化,

C也要对类A子对象初始化。

eg、虚基类注意:对非虚基类,不能在C中对A类子对象初始化。AB1B2C§9、4类得虚函数和多态性对于相同得函数调用,依赖每个对象自己做出恰当得响应,这就是多态性得关键思想。多态性(polymorphism):不同对象在接收同样得消息时,所做出得响应不同。多态性得实现依赖于动态联编。指调用名称相同得成员函数指不同派生类得成员函数一、滞后联编

联编:指一个标识符名和一个存储地址联系在一起得过程。

1、静态联编(早期联编)指:联编工作出现在编译连接阶段,因为这种联编过程在程序开始运行之前完成,所以又称早期联编。在编译时就解决了程序中得操作调用与执行该操作代码间得关系,确定这种关系又被称为束定,因此,静态联编又称为静态束定。

2、动态联编(滞后联编)指:在程序执行时进行得联编,称动态联编,或称动态束定,又叫滞后联编。动态联编时在不同阶段被束定得类得对象将就是不同得。动态联编得实现:C++中,在虚函数得支持下实现。二、虚函数得定义1、定义:virtual成员函数原型;2、注意:

(1)只有在类得继承关系中才存在虚函数。在基类中声明某成员函数为虚函数,则派生类中(名字、参数个数、参数类型和返回值类型都)相同得成员函数都视作虚函数。

(2)使用虚函数须利用赋值兼容规则,要求public继承方式。

(3)多态性使我们编写得程序在处理同一个类层次结构下类得对象时好像她们就是基类得所有对象一样。三、虚函数得使用多态性就是利用基类得指针句柄和基类得引用句柄,而不就是利用名字句柄。实现多态得步骤:

(1)定义基类指针

(2)用该指针指向基类或派生类对象

(3)当程序发送虚函数消息时:若指针指向基类对象,系统调用基类成员函数,若指针指向派生类对象,系统调用派生类成员函数。

(4)若没有步骤(2)中赋值,系统按早期联编方式调用基类成员函数。classX1{intx;public:X1(intxx=0){x=xx;}virtualvoidOutput(){cout<<“x=“<<x<<endl;}};classY1:publicX1{inty;public:Y1(intxx=0,intyy=0):X1(xx){y=yy;}virtualvoidOutput(){X1::Output();cout<<“y=“<<y<<endl;}};可以去掉这里得virtual例:虚函数classZ1:publicX1{intz;public:Z1(intxx=0,intzz=0):X1(xx){z=zz;}virtualvoidOutput(){X1::Output();cout<<“z=“<<z<<endl;}};voidmain(){X1a(5);Y1b(6,7);Z1c(8,9);X1*p[3]={&a,&b,&c};for(inti=0;i<3;i++){p[i]->Output();cout<<endl;}}结论:定义了虚函数后,通过基类指针调用虚函数时,实际被调用得就是指针所指对象内得函数;结果:x=5

x=6

Y=7

x=8

z=9不定义虚函数时得结果:x=5

x=6

x=8基类子对象派生类专有部分派生类虚函数得调用说明:使用基类指针指向其派生类得对象;相同得调用方式实际上能够调用一组中得任何一个派生类中得虚函数,即就是多态性。由于被实际调用得函数就是在运行时确定得,因此称为动态绑定虚函数得使用限制:只能通过指针或引用调用虚函数;如果用普通对象名调用虚函数,将釆用静态联编方式;虚函数定义时,函数名、返回类型、参数类型、个数和顺序与基类中得函数定义应完全一致。(不同于重载函数)构造函数不能定义为虚函数,但析构函数可以。voidmain(){Bb;b、act2();}考虑:

1、程序结果就是什么?

2、如果将A中act2()实现为:

voidA::act2(){this->act1();}

结果如何?

3、如果将A中act1()前得virtual

去掉,结果如何?例:#include<iostream、h>classA{public:virtualvoidact1();voidact2(){act1();}};voidA::act1(){cout<<“A::act1()called、”<<endl;}classB:publicA{public:voidact1();};voidB::act1(){cout<<“B::act1()called、”<<endl;}B::act1()called、B::act1()called、A::act1()called、可以由成员函数调用虚函数四、构造函数中调用虚函数#include<iostream、h>classA{public:A(){}virtualvoidf(){cout<<“A::f()called、\n”;}};classB:publicA{public:B(){f();}voidg(){f();}};classC:publicB{public:C(){}virtualvoidf(){cout<<“C::f()called、\n”;}};voidmain(){Cc;c、g();}结果:A::f()called、C::f()called、结论:构造函数中调用虚函数时,釆用静态联编,即构造函数调用得虚函数就是自己类中实现得虚函数,如果自己类中没有实现这个虚函数,则调用基类中得虚函数。例:定义普通析构函数#include<iostream、h>classAA{inti;public:AA(intn):i(n){}~AA(){cout<<“AA“<<endl;}};classBB:publicAA{char*p;public:BB(intn,char*s):AA(n){p=newchar[strlen(s)+1];strcpy(p,s);}~BB(){deletep;cout<<“BB”<<endl;}};voidmain(){AA*p=newBB(5,”abcd”);

deletep;

};结果:AA普通析构函数在使用delete删除一个对象时,就是静态联编,关联得只就是基类得析构函数。五、虚析构函数#include<iostream、h>classAA{inti;public:AA(intn):i(n){}virtual~AA(){cout<<“AA“<<endl;}};classBB:publicAA{char*p;public:BB(intn,char*s):AA(n){p=newchar[strlen(s)+1];strcpy(p,s);}~BB(){deletep;cout<<“BB”<<endl;}};voidmain(){AA*p=newBB(5,”abcd”);

deletep;

};结果:BB

AA虚析构函数得目得在于使用delete删除一个对象时,就是动态联编,关联得就是子类得析构函数。例:定义虚析构函数六、纯虚函数与抽象类软件使用者:某软件只要用到一些具体事物得类(如:Sphere,Cylinder,Cone类)

软件设计者:增加一个更抽象得基类效果更好(代码重用)(如:Circle类)--某些成员函数在基类中得实现变得没意义(如:Area(),Volume()函数),但其声明仍有意义。

问题:能否将这样得成员函数在基类中只作声明,而其实现留给派生类呢?

用纯虚函数将基类改造为抽象类1、纯虚函数定义:

virtual返回类型函数名(参数表)=0纯虚函数在基类中声明,具体实现在派生类中。2、抽象类包含纯虚函数得类称为抽象类。抽象类为其所有子类提供了统一得操作界面。说明:抽象类不能实例化,即不能声明抽象类对象;抽象类只作为基类被继承,无派生类得抽象类无意义可定义指向抽象类得指针或引用,她们必然指向派生类对象,从而实现多态性。例:纯虚函数与抽象类classShape{public:virtualdoublearea()=0;};classPoint{doublex,y;public:Point(doublex0=0、0,doubley0=0、0):x(x0),y(y0){}doublegetX(){returnx;}doublegetY(){returny;}};classRectAngle:publicShape{Pointul;//左下角顶点

Pointdr;//右上角顶点public:RectAngle(Pointp1,Pointp2):ul(p1),dr(p2){}virtualdoublearea(){return(dr、getX()-ul、getX())*(dr、getY()-ul、getY());

}PointgetUpleft(){returnul;}PointgetDownright(){returndr;}};classCircle:publicShape{Pointcen;//圆心

doublerad;//半径public:Circle(Pointc,doubler):cen(c),rad(r){}virtualdoublearea(){return3、1415926*rad*rad;}PointgetCenter(){returncen;}doublegetRadius(){returnrad;}};voidshowArea(Shape&s){cout<<s、area();}voidmain(){RectAngle

r(Point(2、0,2、0),Point(8、0,8、0));showArea(r);cout<<endl;Circlec(Point(8,8),4);showArea(c);}通过调用showArea()这同一动作,计算出不同形状得面积,即同一消息,不同响应-----多态性。类非static数据成员函数成员对象1非static数据成员对象2非static数据成员对象n非static数据成员类数据成员函数成员static数据成员对象1对象2对象n对象3§9、5类得静态成员在类成员声明前加上static修饰即把该成员声明为该类得静态成员;static数据成员静态数据成员必须在定义第一个对象之前作为静态变量加以定义并初始化;静态函数

温馨提示

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

评论

0/150

提交评论