第8章-继承和派生_第1页
第8章-继承和派生_第2页
第8章-继承和派生_第3页
第8章-继承和派生_第4页
第8章-继承和派生_第5页
已阅读5页,还剩32页未读 继续免费阅读

下载本文档

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

文档简介

第8章继承和派生

在C++中可重用性是通过继承(inheritance)这一机制来实现的。继承是C++的一个重要组成部分。8.1继承和派生的概念

在C++中,所谓“继承”就是在一个已存在的类的基础上建立一个新的类。一个新类从已有的类那里获得其已有特性,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。从另一角度说,从已有的类(父类)产生一个新的子类,称为类的派生。类的继承是用已有的类来建立专用类的编程技术。8.1.1基类与派生类

1.继承与派生交通工具飞机滑翔机直升飞机喷气式飞机交通工具飞机滑翔机直升飞机喷气式飞机已存在的类(例如“交通工具”)称为“基类”或“父类”。新建立的类(例如“飞机”)称为“派生类”或“子类”。一个基类可以派生出多个派生类,每一个派生类又可以作为基类再派生出新的派生类,因此基类和派生类是相对而言的。2.基类与派生类请注意图中箭头的方向,在本书中约定,箭头表示继承的方向,从派生类指向基类。8.1.2继承与派生的作用1.避免公用代码的重复开发,在减少代码和数据冗余的同时,节省程序开发的时间,提高程序开发的效率和质量。2.通过增强代码一致性来减少模块间的接口和界面。3.通过层次关系来组织对象,使得高层类作为低层类的抽象,有利于程序员掌握对象的共性,在此基础上,就能更快地掌握低层类中的个性特征,给编程与代码重用带来方便。8.1.3派生类的声明声明一个派生类的一般格式为:class派生类名:派生存取说明符(可省)基类名{ //派生类新增的数据成员和成员函数};

派生类名”是要从基类派生出的新类的名字,“基类名”是一个已经定义好的基类的名字,“派生存取说明符”可以是关键字public或protected或private,派生存取说明符的作用会在后面详细介绍,“派生存取说明符”可以省略,默认为private,即私有派生。//定义一个基类(Student类)classStudent{ public: charname[10];//姓名 intage; //年龄 charsex; //性别 charclassname[20]; //班级名称};//定义一个派生类(GraduateStudent类)classGraduateStudent:publicStudent{ public: charAdvisorname[10];//导师 intqualifiergrade; //资格考试分};8.2派生类成员的访问控制

8.2.1派生类成员访问控制简介从一个基类派生出一个子类时,可以通过指定派生时的存取说明符来实现对派生类成员的访问控制。既然派生类中包含基类成员和派生类自己增加的成员,就产生了这两部分成员的关系和访问属性的问题。在建立派生类的时候,并不是简单地把基类的私有成员直接作为派生类的私有成员,把基类的公用成员直接作为派生类的公用成员。实际上,对基类成员和派生类自己增加的成员是按不同的原则处理的。8.2.2private、protected与public类成员

1.private类成员一个类中的私有(private)成员(包括数据成员和成员函数)只能被它们所在类的成员函数和友元函数访问,在C++中类成员存取说明符缺省的情况下的存取特征都是私有的(private)。【例8.1】类私有成员的定义和访问#include<iostream.h>classPerson { //Person类的三个私有成员 charname[10];//姓名 private: intage; //年龄 charsex; //性别}; main() { PersonP1; P1.age=20; //非法 cout<<P1.name<<endl; //非法 return0; }2.public类成员

一个类中的公有(public)成员(包括数据成员和成员函数)可以被程序中任何代码(包括函数)访问,一般情况下,应尽量将类的数据成员声明为私有(private),然后为需要被类外部访问的数据成员提供公有的成员函数,实现对私有成员的设置和访问,这种结构能够向类的客户很好的隐藏实现方法,在有效减少错误的同时,可以增强程序的可修改性。3.protected类成员使用protected声明的称为保护成员。任何一个类的保护成员仅可以被其自己和派生类的所有非静态成员函数和友元函数直接访问,也就是说其他的外部函数是不能访问它的。因此,对于那些既要对外界隐藏,又要能被派生类访问的成员,可以将它们声明为保护成员。

如果基类声明了私有成员,那么任何派生类都是不能访问它们的,若希望在派生类中能访问它们,应当把它们声明为保护成员。如果在一个类中声明了保护成员,就意味着该类可能要用作基类,在它的派生类中会访问这些成员。8.2.3三种派生方式的定义

1.私有(private)派生

在声明一个派生类时将派生方式指定为private的,称为私有派生,用私有派生方式建立的派生类称为私有派生类,其基类称为私有基类。由私有派生得到的派生类,它从基类继承的成员都将变为私有成员,也就是说通过私有派生,派生类从基类继承来的公有成员和保护成员都将变成派生类的私有成员,这些成员将只能被派生类的成员函数和友元函数访问。需要重点说明的是,基类的私有成员经过私有派生后继续保持其基类的私有成员身份,无法被派生类访问。

2.保护(protected)派生由保护派生得到的派生类,它从基类继承的公有和保护成员都将变为派生类的保护成员。基类的私有成员经过保护派生后继续保持其基类的私有成员身份,依然无法被派生类访问。保护派生的特点是:基类的公用成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有。也就是把基类原有的公用成员也保护起来,不让类外任意访问。3.公有(public)派生由公有派生得到的派生类,它从基类继承的成员都将维持原有访问控制特征,即通过公有派生后,派生类从基类继承的公有成员在派生类中仍然是公有成员,保护成员仍然是保护成员,而基类的私有成员经过公有派生后继续保持其基类的私有成员身份,依然无法被派生类访问。8.2.4派生类成员访问控制规则

1.派生类中新增成员的访问控制遵循类成员访问控制的规则。存取说明符访问控制规则Private此派生类的非静态成员函数和友元函数可以直接访问Protected此派生类和其子类非静态成员函数和友元函数可以访问Public任何非静态成员函数,友元函数和非成员函数都可以直接访问2.类成员在派生类中的访问控制规则基类成员的存取说明符派生方式public派生protected派生private派生Private在派生类中被隐藏在派生类中被隐藏在派生类中被隐藏Protected派生类中为protected派生类中为protected派生类中为privatePublic派生类中为public派生类中为protected派生类中为private注意:不管是何种派生,基类的private成员永远都不能被其派生类直接访问,但可以通过基类的public和protected成员函数访问。

8.3派生类的构造函数和析构函数8.3.1派生类的构造函数和析构函数的声明类的构造函数是类的一种特殊的成员函数,它的作用主要是为对象分配内存、进行初始化;而析构函数它的作用与构造函数相反。派生类的构造函数声明格式如下:派生类的构造函数名称(参数表):基类的构造函数名(参数表){ //… //派生类构造函数体}classPoint{intx,y; public: Point(inta,intb) //基类的构造函数 { x=a; y=b; cout<<”ConstructingPointclass\n”; } ~Point() //基类的析构函数 { cout<<”DestructingPointclass\n”;}};classCircle:publicPoint{ doubleradius; public: Circle(doubler,inta,intb):Point(a,b)//派生类的构造函数 { radius=r; cout<<”ConstructingCircleclass\n”; } ~Circle() //派生类的析构函数 { cout<<”DestructingCircleclass\n”; }};派生类中含有对象成员时,其构造函数的声明格式如下:

派生类的构造函数名称(参数表):基类的构造函数名(参数表),对象成员名1(参数表),…对象成员名N(参数表){ //… //派生类构造函数体}classCircle2:publicPoint{ doubleradius; PointP1; public:Circle2(doubler,inta,intb):Point(a,b),P1(a,b) //派生类的构造函数,定义时//指定了基类的构造函数和对象成员P1的构造函数 { radius=r; cout<<”ConstructingCircle2class\n”; } ~Circle2() //派生类的析构函数 { cout<<”DestructingCircle2class\n”; }};8.3.2派生类的构造函数和析构函数的构造规则

1.基类具有显式的构造函数时(1)基类具有不带参数的构造函数派生类既可以自己不定义构造函数,也可以根据需要定义自己的构造函数,构造函数可以带参数也可以省略,在派生类中定义构造函数时还可省略“:基类构造函数名(参数表)”。(2)基类仅有带参数的构造函数派生类必须显式的定义其构造函数,并在声明时指定其基类的某一构造函数和参数表,把参数传递给基类构造函数。2.基类具有隐式的构造函数时当基类没有显式的定义(即隐式的定义)构造函数时,派生类可以根据需要定义自己的构造函数,派生类构造函数的参数表、基类的构造函数名和参数表都可以根据需要省略。8.3.3派生类构造函数和析构函数的调用顺序1.构造函数的调用顺序派生类对象创建的时候,构造函数的执行顺序为:(1)基类的构造函数(2)对象成员的构造函数(3)派生类自身的构造函数2.析构函数的调用顺序与构造函数的调用顺序正好相反。即

(1)派生类自身的析构函数(2)对象成员的构造函数(3)基类的析构函数【例8.6】派生类Circle2的构造函数和析构函数的执行顺序//…Point和Circle2类的定义main(){ Circle2C1(0.0,1,1); cout<<”-------------programline----------------\n”; return0;}8.4多重继承8.4.1多重继承的声明前面讨论的是单继承,即一个类是从一个基类派生而来的。在C++中还允许从多个基类中派生出新的子类,这种派生方法称为多重继承(或多基派生)。这一强大的派生功能可以大大提高软件重用的灵活性。声明的格式如下:class派生类名:派生存取说明符(可省)1基类名1,…,派生存取说明符N基类名N{ //派生类新增的数据成员和成员函数};【例8.7】多重继承的声明#include<iostream.h>classCircle1//基类Circle1{protected:intr;public:voidsetx(intx){r=x;}voiddraw(){ cout<<"drawing…\n";}};classCircle2//基类Circle2{protected:intr;public:voidsetx(intx){r=x;}voidwrite(){ cout<<"writing…\n";}};classCircle:publicCircle1,publicCircle2//类Circle公有继承了Circle1和Circle2{public:voidshow(){ cout<<"showing…\n";}};voidmain(){Circlecc;cc.draw();cc.write();cc.show();}8.4.2多重继承的几点说明1.在多重继承中派生类成员的访问控制规则与单一继承规则相同。2.多重继承的构造函数与析构函数定义与单一继承相似,只是在构造函数定义时N个基类的构造函数之间用逗号分隔。3.多重继承虽然功能强大,但也容易造成系统的复杂性,设计时务必正确和谨慎,能用单一继承时尽量不用多重继承。4.多重继承容易产生模糊性,在引用时要注意例如前面例8.7如果按照下面来引用:voidmain(){Circlecc;cc.setx(10);//Circle1的setx还是Circle2的setx?}

voidmain(){Circlecc;cc.Circle1::setx(10);//说明是Circle1中的r为10}8.4.3虚基类

1.虚基类的概念当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类.

2.虚基类的声明class派生类名:virtual派生存取说明符(可省)虚基类名{ //派生类新增的数据成员和成员函数};3.虚基类的初始化虚基类初始化时,构造函数的调用顺序规则:(1)同一层派生中包含多个虚基类时,虚基类的构造函数按它们派生时声明的先后次序调用。(2)如某虚基类是由实基类派生而来,则先调用此实基类的构造函数,再调用虚基类的构造函数,最后才是派生类的构造函数。(3)若同一层派生中,同时存在虚基类与实基类,应先调用虚基类的构造函数,再调用实基类的构造函数,最后调用派生类的构造函数。4.虚基类的作用虚基类的作用就是为其他类提供一个合适的基类,以便派生类可以从它那里继承和实现所需的接口。在多重继承时,当派生类的多个基类有一个共同的基类时,为防止产生二义性问题可使用虚基类方法。注意:虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。【例8.11】存在二义性的虚基类多重派生#include<iostream.h>classBase //基类{ protected: intb;public:Base() {b=1;cout<<”ConstructingBaseclass”<<endl; }};classBase1:publicBase//基类1从Base派生{ public:Base1() {cout<<”ConstructingBase1class”<<endl;cout<<”Baseb=”<<b<<endl; }};classBase2:publicBase //基类2从Base派生{ public:Base2() {cout<<”ConstructingBase2class”<<endl;cout<<”Baseb=”<<b<<endl; }};classDerived:publicBase1,Base2//从两个基类base1和base2多重派生出子类{ public:Derived() { cout<<”ConstructingDerivedclass”<<endl;cout<<”Baseb=”<<b<<endl; }};main(){ DerivedD1; return0;}改成如下语句:classBas

温馨提示

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

评论

0/150

提交评论