版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第2章继承与派生本章学习重点掌握内容: 继承的概念 派生类的建立及继承的方式 各种继承方式下基类成员的访问机制 派生类的构造函数和析构函数 多重继承 虚基类8/16/20231第2章继承与派生本章学习重点掌握内容:8/4/2023第2章继承与派生2.1继承与派生的基础知识2.2类的继承方式2.3派生类的构造函数与析构函数2.4基类与派生类的转换2.5多重继承2.6虚基类2.7综合应用实例8/16/20232第2章继承与派生2.1继承与派生的基础知识8/42.1继承与派生的基础知识2.1.1继承与派生的基本概念现实世界中,许多事物之间的并不是孤立存在的,它们存在共同的特性,有细微的差别,可以使用层次结构描述它们之间的关系。例如交通工具的层次结构如图2.1所示:8/16/202332.1继承与派生的基础知识2.1.1继承与派生的基本概念2.1.1继承与派生的基本概念C++通过类派生(ClassDerivation)的机制支持继承(Inheritance)。允许程序员在保持原有类特性的基础上进行扩展,增加功能,派生出新类。继承是面向对象程序设计中的代码复用的最重要的手段之一。被继承的类称为基类(BaseClass)、父类或超类(Superclass),而新产生的类称为派生类(DerivedClass)或子类(Subclass)。基类和派生类的集合称作类继承层次结构(Hierarchy),继承呈现了面向对象程序设计的层次结构。8/16/202342.1.1继承与派生的基本概念C++通过类派生(2.1.1继承与派生的基本概念
一个新类从已有的类获得其已有的特性称为继承。通过继承,新类获得了父类的所有数据成员和成员函数,并可以添加自己的数据成员和成员函数。一个基类可以派生出很多的子类,一个子类也可以作为另一个新类的基类,因此基类和子类是相对而言的。继承的方式有以下2种:单一继承和多重继承。8/16/202352.1.1继承与派生的基本概念一个新类从已有的类获2.1.1继承与派生的基本概念单一继承和多重继承请注意图中箭头的方向,本书约定,箭头表示继承的方向,由子类指向基类。8/16/202362.1.1继承与派生的基本概念单一继承和多重继承请注意图中2.1.2派生类的定义定义派生类的一般格式为:class派生类名:继承方式基类名{private: 成员表1;//派生类增加或重写的私有成员protected: 成员表2;//派生类增加或重写的保护成员public: 成员表3;//派生类增加或重写的公有成员};
8/16/202372.1.2派生类的定义定义派生类的一般格式为:8/4/202.1.2派生类的定义其中:基类名是已声明的类,派生类名是新生成的类名; 继承方式规定了如何访问从基类继承的成员。继承的方式包括:私有继承(private)、保护继承(protected)、公有继承(public)。不同的继承方式下,派生类继承的父类成员的访问权限是不同的。继承方式可以省略不写,默认的继承方式为私有继承(private); 派生类的定义中包括子类新增加的成员和继承父类需要重写的成员。新添加的成员是派生类对基类的发展,说明派生类新的属性和方法;派生类继承了父类的数据成员和成员函数,有时继承来的成员函数需要改进,以满足新类的实际需要。C++允许在派生类中重新声明和定义这些成员函数,使这些函数具有新的功能,称之为重写或覆盖。重写函数起屏蔽、更新作用,取代基类成员,完成新功能。8/16/202382.1.2派生类的定义其中:基类名是已声明的类,派生类名是2.1.2派生类的定义【例2.1】已知盒子CBox类,用继承与非继承两种不同的方法定义彩色盒子CColorbox类。分析:盒子类(Cbox)具有长、宽和高,成员函数SetLength()、SetWidth()和SetHeight()分别设置盒子的长、宽和高,成员函数Volume()计算盒子的体积。彩色盒子除具有以上特性外,还有一个数据成员color表示盒子的颜色,相应的成员函数SetColor()用于设置彩色盒子的颜色。8/16/202392.1.2派生类的定义【例2.1】已知盒子CBox类,用继2.1.2派生类的定义非继承的方式,分别定义CBox类和CColorbox类盒子类的定义:代码见备注彩色盒子类的定义:代码见备注8/16/2023102.1.2派生类的定义非继承的方式,分别定义CBox类和C2.1.2派生类的定义使用派生类定义:classCColorbox:publicCBox//公有继承{ //新增的私有数据成员 public: voidSetColor(intc)//新增的成员函数 { color=c; } private: intcolor; };利用继承机制产生类比第一种简单多了,但功能一样。派生类CColorbox公有继承Cbox类,它包括基类CBox类的全部数据成员(length,width,height)和成员函数(SetWidth、SetHeigh和SetWidth),但访问权限发生了变化。并且添加自己的新成员——数据成员color和成员函数SetColor()。8/16/2023112.1.2派生类的定义使用派生类定义:8/4/2023112.1.3派生类的生成仔细分析派生新类这个过程,实际是经历了以下步骤:首先继承基类的成员,不论是数据成员,还是成员函数,除构造函数与析构函数外全部接收,全部成为派生类的成员。第二步是重写基类成员。当基类成员在派生类的应用中不合适时,可以对继承的成员加以重写。如果派生类声明了一个与基类成员函数相同的成员函数时,派生类中的新成员则屏蔽了基类同名成员,类似函数中的局部变量屏蔽全局变量。称为同名覆盖(Override)。第三步定义新成员。新成员必须与基类成员不同名,是派生类自己的新特性。派生类新成员的加入使得派生类在功能上有所发展。这一步是继承与派生的核心特征。第四步是重写构造函数与析构函数。因为派生类不继承基类的构造函数与析构函数,并且派生类的需要对新添加的数据成员进行必要的初始化,所以构造函数与析构函数需要重写。8/16/2023122.1.3派生类的生成仔细分析派生新类这个过程,实际是经历2.2 类的继承方式派生类中包含基类的成员和派生类自己增加的成员,那么这两部分的成员关系和访问权限该如何确定呢?在继承机制中,并不是简单的把基类的私有成员直接作为派生类的私有成员,把基类的公有成员直接作为派生类的公有成员。派生类继承的基类成员访问权限由继承方式来控制。继承方式有三种:public(公有)继承、protected(保护)继承和private(私有)继承。不同的继承方式,决定了从基类继承来的成员的访问权限。下面分别介绍不同继承方式下,派生类成员的访问权限。8/16/2023132.2 类的继承方式派生类中包含基类的成员和派生2.2.1公有继承
当定义一个派生类时,将基类前的继承方式指定为public,则称为公有派生(或公有继承)。采用公有继承方式时,基类的公有成员和保护成员的访问权限在派生类中不变。而基类的私有成员在派生类中是不可访问。但它仍然是基类的私有成员,如果需要在派生类中引用继承基类的私有成员,那么需要通过基类的公有或保护的成员函数访问。【例2.3】演示公有继承方式下,不同成员的访问权限。程序代码见备注:8/16/2023142.2.1公有继承 当定义一个派生类时,将基类前的继承方式2.2.1公有继承【例2.4】公有派生方式下如何访问继承的基类原有私有数据成员。程序代码见备注:8/16/2023152.2.1公有继承【例2.4】公有派生方式下如何访问继承的2.2.2私有继承当定义一个派生类时,将基类前的继承方式指定为private,则称为私有继承。用私有继承方式建立的派生类称为私有派生类,其基类成为私有基类。采用私有继承方式时,私有基类的公有成员和保护成员在私有派生类中成为私有成员。即派生类成员可访问它们,而派生类外不可访问它们。基类的私有成员在派生类中成为不可访问的成员。私有继承基类成员的访问权限如表2-2所示。私有继承的意义是将基类中原来能被外部访问的成员隐藏起来,不让外界引用。【例2.5】私有继承演示。8/16/2023162.2.2私有继承当定义一个派生类时,将基类前的继承方2.2.2私有继承由上例可以看到私有继承方式:(1)不能通过派生类对象(box1)引用从私有继承过来的任何成员。如box1.set(3,5,6);或box1.length=100。(2)在派生类内部(如派生类的成员函数),不可以访问基类的私有成员(如length=len,length为基类的私有成员),但可以访问基类的公有和保护成员(如height=h,height为基类的保护成员)。(3)如果派生类需要访问基类的私有成员,可以通过派生类的成员函数调用基类的公有成员函数实现如:voidset_1(doublelen,doublew,doubleh,intc){set(len,w,h);//基类的公有成员函数……}由上可以看出,私有派生的限制太多,一般不经常使用。8/16/2023172.2.2私有继承由上例可以看到私有继承方式:8/4/202.2.3保护继承当定义一个派生类时,将基类前的继承方式指定为protected,则称为保护继承。在保护继承中,基类的公有成员和保护成员成为派生类的保护成员,在派生类中可以直接访问,但在派生类外不能直接访问任何基类成员的。基类中的私有成员成为派生类的不可访问成员,在派生类中不可直接访问。保护继承基类成员的访问权限如表2-3所示。保护继承的意义是将基类的公有成员也保护起来,不让类外部任意访问。8/16/2023182.2.3保护继承当定义一个派生类时,将基类前的继承方式指继承的方式有三种,使用不同继承方式,基类的成员在派生类中的访问权限也不同。不同继承方式下基类成员在派生类的访问权限总结如表2-4所示。2.2.4继承方式的总结和比较8/16/202319继承的方式有三种,使用不同继承方式,基类的成员在派生类中的访2.3派生类的构造函数与析构函数派生类的成员是由基类中的数据成员和派生类中新增的数据成员共同构成。而在继承机制下,构造函数不能够被继承。因此,对继承过来的基类成员的初始化工作也得由派生类的构造函数完成。也就是说在定义派生类的构造函数时,既要初始化派生类新增数据,又要初始化基类的成员。所以,在定义派生类的构造函数时,有两步需要做:编写代码完成自己的数据成员进行初始化调用基类构造函数使基类数据成员得以初始化。8/16/2023202.3派生类的构造函数与析构函数派生类的成员是由2.3.1简单派生类的构造函数单一继承的构造函数的定义形式为:派生类名:派生类构造函数名(参数总表):基类构造函数名(参数名表){派生类新增成员的初始化语句};
定义派生类的构造函数时,在构造函数的参数总表中包括基类构造函数所需的参数和派生类新增的数据成员初始化所需的参数。冒号后面基类构造函数名(参数名表),表示要调用基类的构造函数。【例2.6】演示派生类的构造函数执行顺序。8/16/2023212.3.1简单派生类的构造函数单一继承的构造函数的定义形2.3.2析构函数
析构函数的功能是做善后工作,析构函数无返回类型也没有参数,情况比较简单。在派生过程中,基类的析构函数不能继承,如果需要析构函数的话,要在派生类中重新定义。派生类析构函数定义格式与非派生类无任何差异,只要在函数体内把派生类新增一般成员处理好就可以了。而对基类成员的善后工作,系统会自己调用基类的析构函数来完成。如果没有显示的定义析构函数,系统会自动生成一个默认的析构函数,清理工作就是靠它们来完成的。析构函数各部分执行次序与构造函数相反,首先对派生类新增成员析构,然后对基类成员析构。8/16/2023222.3.2析构函数析构函数的功能是做善后工作,析构2.3.3复杂派生类的构造函数和析构函数一个派生类中新增加的成员可以是简单的数据成员,也可以是类对象。派生类可以是单一继承,也可以是多重继承。假如派生类是多重继承,并且新增数据成员有一个或多个类对象,那么派生类需要初始化的数据有三部分:继承的成员、新增类对象的成员和新增普通成员。这种复杂派生类的构造函数定义如下:派生类名::派生类构造函数名(总参数表):基类构造函数名1(参数表1),基类构造函数名2(参数表2),……子对象名1(参数表n),子对象名2(数表n+1)……{派生类新增普通数据成员的初始化;}8/16/2023232.3.3复杂派生类的构造函数和析构函数一个派生类2.3.3复杂派生类的构造函数和析构函数派生类构造函数的调用顺序如下:基类构造函数。按它们在派生类定义中的先后顺序,依次调用。子对象的构造函数。按它们在派生类定义中的先后顺序,依次调用。派生类的构造函数。复杂派生类的析构函数,只需要编写对新增普通成员的善后处理,而对类对象和基类的善后工作是由类对象和基类的析构函数完成的。析构函数的调用顺序与构造函数相反。【例2.7】复杂继承举例。8/16/2023242.3.3复杂派生类的构造函数和析构函数派生类构造函数的调2.3.3复杂派生类的构造函数和析构函数【例2.7】复杂继承举例。8/16/2023252.3.3复杂派生类的构造函数和析构函数【例2.7】复杂继2.3.3复杂派生类的构造函数和析构函数在派生类构造函数使用中应注意以下问题:(1)派生类构造函数的定义中可以省略对基类构造函数的调用,其条件是在基类中必须有缺省的构造函数或者根本没有定义构造函数。当然,基类中没有定义构造函数,派生类调用基类的缺省构造函数。(2)当基类的构造函数使用一个或多个参数时,则派生类必须定义构造函数,提供将参数传递给基类构造函数途径。在有的情况下,派生类构造函数的函数体可能为空,仅起到参数传递作用。8/16/2023262.3.3复杂派生类的构造函数和析构函数在派生类构造函数使2.4基类与派生类的转换基类与派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,因此可以将派生类的值赋给基类对象,在用到基类对象的时候可以用其子类对象代替。具体表现在以下几个方面:派生类对象可以向基类对象赋值可以用子类(即公用派生类)对象对其基类对象赋值。如Cbox与CColorbox:CBoxbox;//定义基类CBox对象boxCColorboxcolorbox;//定义类Ccolorbox的对象colorboxbox=colorbox;//用派生类B对象b1对基类对象a1赋值在赋值时舍弃派生类自己的成员。实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题。
8/16/2023272.4基类与派生类的转换基类与派生类对象之间有赋值兼容关系2.4基类与派生类的转换(2)派生类对象可以向基类对象的引用进行赋值或初始化CBoxbox;//定义基类CBox对象boxCColorboxcolorbox;//定义派生类Ccolorbox的对象colorboxCBox&r1=box;这时,引用变量r1是box的别名,r1和box共享同一段存储单元。也可以用子类对象初始化基类的引用变量,例如:CBox&r2=colorbox;或者保留上面第3行“CBox&r1=box;”,而对r1重新赋值:r1=colorbox;注意:将派生类对象赋值给基类引用。此时基类引用并不是派生类对象的别名,也不与派生类共享同一段存储单元。它只是派生类中基类部分的别名,基类引用与派生类中基类部分共享同一段存储单元。例如:CBox&r2=colorbox;r2与colorbox具有相同的起始地址。8/16/2023282.4基类与派生类的转换(2)派生类对象可以向基类对象的2.4基类与派生类的转换(3)派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象。例如:CBoxbox;//定义基类CBox对象boxCColorboxcolorbox;//定义类CBox的公用派生类Ccolorbox的对象colorboxCBox*pt=&box;//定义基类CBox对象指针变量pt并指向boxpt->SetHeight(10);//调用box.SetHeight()函数pt=&colorbox;//将派生类基类地址赋值给基类指针,即pt指向colorboxpt->SetHeight(10);//调用colorbox.SetHeight()函数8/16/2023292.4基类与派生类的转换(3)派生类对象的地址可以赋给指2.5多重继承前面主要介绍了单一继承,仅仅提到多重继承的概念。在现实世界中,很多时候一个类会有两个或两个以上的基类。例如沙发床,既继承了床的特性又继承了沙发的特性。沙发床的多重继承结构图如图2.7所示。C++中,定义派生类时,派生类有两个或多个基类称为多重继承。8/16/2023302.5多重继承前面主要介绍了单一继承,2.5多重继承2.5.1多重继承的定义多重继承可以看作是单一继承的扩展,多重继承的定义格式如下:class派生类名:继承方式1基类名1,继承方式2基类名,……{ public: 新增加的公有成员; protected: 新增加的保护成员;private: 新增加的私有成员; };8/16/2023312.5多重继承2.5.1多重继承的定义8/4/20232.5多重继承多重继承派生类的构造函数格式如下:派生类名::派生类构造函数名(总参数表):基类名1(参数表1),基类名2(参数表2){派生类构造函数体}其中,总参数表中各个参数包含了其后基类的各个分参数表。多重继承下派生类的构造函数与单一继承下派生类构造函数相似,它必须同时负责该派生类所有基类构造函数的调用。同时,派生类的参数必须包含完成所有基类初始化所需的参数。8/16/2023322.5多重继承多重继承派生类的构造函数格式如下:8/4/2.5.1多重继承的定义【例2.8】设计沙发床类。分析:床类可以用来睡觉Sleep(),沙发类可以用来看电视WatchTV()。沙发床具有床和沙发两者的特特性,沙发床还有自己的特性——折叠FoldOut()。因此先定义床类和沙发类,沙发床类由这两个类派生。然后添加沙发床自己的特性。程序代码如下:8/16/2023332.5.1多重继承的定义【例2.8】设计沙发床类。8/4/2.5.1多重继承的定义沙发床类继承了床类和沙发类,具有了沙发和床的特性,因此可以使用派生类对象ss调用床的Sleep()成员函数,完成睡觉功能;通过调用沙发WatchTV()成员函数完成看电视功能;调用新增的FoldOut()可以完成折叠与打开功能。派生类对象调用构造函数的顺序是先基类的构造函数再派生类的构造函数,有两个基类(Cbed和Csofa)时,基类构造函数调用的顺序是按照声明派生类时基类的排列顺序来进行。
8/16/2023342.5.1多重继承的定义沙发床类继承了床类和沙发类,具有了2.5.2多重继承中的二义性问题多重继承反映了现实生活中的情况,使一些复杂的问题简单化,提高了程序开发效率。但多重继承中存在两类二义性问题:1.调用不同基类中相同成员时产生的二义性派生类的多个基类之间出现相同的成员,则在派生类中访问此成员时会出现二义性。8/16/2023352.5.2多重继承中的二义性问题多重继承反2.5.2多重继承中的二义性问题解决多重继承中调用不同基类中相同成员时产生的二义性可以使用以下方法:(1)使用域作用符解决二义性问题可以使用::域作用符对此成员函数加以区分:ob1.CBed::SetWeight(100);//调用基类Cbed的函数成员SetWeight()ob1.CSofa::SetWeight(200);//调用基类CSofa的函数成员SetWeight()使用域作用符可以消除编译时二义性。程序员需要知道类的继承层次信息,加大程序开发的复杂度。8/16/2023362.5.2多重继承中的二义性问题解决多重继承中调2.5.2多重继承中的二义性问题(2)覆盖函数同名隐藏(覆盖)派生类继承了两个基类的print()函数,派生类在自己的构造函数中又重写了print()函数。如果在主函数如下:voidmain(){ CSleeperSofaob1;//继承了两个基类的特性 ob1.print();//正确,print()是覆盖函数不存在二义性}ob1.print();语句能通过编译,这是因为派生类新增的成员函数print()覆盖了基类中的同名成员,这点与局部变量屏蔽全局变量类似。可以使用域作用符调用基类的成员函数。如ob1.CBed::print(),则调用基类Cbed的print()。8/16/2023372.5.2多重继承中的二义性问题(2)覆盖函数同名隐藏(覆2.5.2多重继承中的二义性问题注意:在不是虚函数的情况下,如果派生类中新增的成员函数与基类的某一成员函数同名,则该函数会隐藏基类中所有该函数的重载函数。#include<iostream.h>classCA{public: voidf(intx){cout<<"thef(int)ofCA!"<<endl;} voidf(){cout<<"thef()ofCA!"<<endl;}};classCB:publicCA{public: voidf(){cout<<"thef()ofCB!"<<endl;}};voidmain(){ CBb; b.f(); b.f(0);}8/16/2023382.5.2多重继承中的二义性问题注意:在不是虚函数的情况下2.5.2多重继承中的二义性问题2.派生类中访问公共基类成员时产生的二义性派生类有多个基类,而这多个基类又从同一个基类派生,则在派生类中访问公共基类成员时会出现二义性。此时引入虚基类解决。8/16/2023392.5.2多重继承中的二义性问题2.派生类中访问公共基类成2.5.2多重继承中的二义性问题【例2.9】一个公共基类在派生类中产生的二义性问题。类B与类C由类A公有派生,而类D由类B与类C公有派生(如图2.9所示),则类D中将包含类A的两个拷贝。这种同一个基类在派生类中产生多个拷贝不仅多占用了存储空间,而且可能会造成二义性问题。8/16/2023402.5.2多重继承中的二义性问题【例2.9】一个公共基类在2.6虚基类在多重继承中,虚基类保证从不同路径继承过来的公共基类成员在内存中就只有一个拷贝,所以可解决同名基类成员产生的二义性问题。2.6.1虚基类的定义虚基类的定义是在派生类的定义过程中定义的,语法格式如下:class派生类名:virtual继承方式基类类名{......};其中,virtual关键字只对紧随其后的基类名起作用。上述定义使得基类为派生类的虚基类,定义了虚基类之后,虚基类的成员在派生过程中和派生类一起维护同一个内存拷贝。【例2.10】用虚基类的方法解决公共基类成员产生的二义性。8/16/2023412.6虚基类在多重继承中,虚基类保证从2.6虚基类(1)采用虚基类后,在D类中只有唯一的数据成员x;所以在建立D类的对象后,调用Print()输出x时,不产生二义性。(2)A类为虚基类以后,公共基类A的构造函数仅被执行一次。因为具有虚基类的派生类构造函数与一般派生类构造函数有所不同。在例2.10中,D的构造函数中增加一个给虚基类的初始化列表项A(a),而给两个直接基类B,C初始化列表项B(a,b),C(d,e)仍然保留。C++为了保证虚基类构造函数仅被执行一次,规定在创建对象的派生类构造函数中优先调用虚基类的构造函数,并在执行后,忽略直接基类初始化列表对虚基类的构造函数的
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 新春售楼营销方案模板(3篇)
- 春节除雪活动方案策划(3篇)
- 校园线上营销推广方案(3篇)
- 河道河提施工方案(3篇)
- 淘宝红酒营销推广方案(3篇)
- 电动吊篮施工方案说明(3篇)
- 砂石土垫层施工方案(3篇)
- 花店活动推动方案策划(3篇)
- 行车地梁施工方案(3篇)
- 超市有趣活动方案策划(3篇)
- 地基桩基公司管理制度
- 郁南县2023年低效油茶林改造项目作业设计
- 《危重症患儿管饲喂养护理》中华护理学会团体标准解读
- 《国家综合性消防救援队伍队列条令(试行)》课件
- 2024年贵州省高考化学试题含答案解析
- 2025-2030年中国核桃种植深加工行业竞争格局与前景发展策略分析报告
- 《冻鲅鱼、冻鲐鱼》标准及编制说明
- 古诗鉴赏(简答题各题型答题技巧与答题规范)-2025年北京高考语文一轮复习(原卷版)
- 加油站施工施工组织设计方案
- 2024年西藏自治区中考语文试题卷
- 汽车配件物流运输服务方案
评论
0/150
提交评论