《《面向对象程序设计》.ppt》由会员分享,可在线阅读,更多相关《《面向对象程序设计》.ppt(36页珍藏版)》请在三一办公上搜索。
1、面向对象程序设计理论篇,武汉大学资源与环境科学学院地理信息科学系,教材及参考资料:,面向对象的理论与C+实践 王燕 编著 清华大学出版社,By:任福 博士Email:renfuRoom:7-501,第十一章:继承与类的派生,要设计可重用性模块,任何方法都必须面对重复和差别,为了避免一再地重写同样的代码,引入不一致的错误,就必须进行抽象,抽象出一般特性后,还需在此基础上扩充其特殊的功能,使之能表达具体的事物。继承是软件重用的一种形式,实现这种形式的方法是从现有类建立新类。新类继承了现有类的属性和行为,并且为了使新类具有自己所需要的功能,新类还要对这些属性和行为予以修饰。,青出于蓝而胜于蓝!,11
2、、派生类的概念,第十一章:继承与类的派生,继承是对象类之间的一种相交关系,是派生关系,具有以下性质:(1)类间的共享特性;(2)类间的细微区别;(3)类间的层次结构。在建立一个新类时,程序员可以让新类继承预定义基类的数据成员和成员函数。这种新类称为派生类,派生类本身也可能成为未来派生类的基类。对于单重继承,派生类只有一个基类。对于多重继承,派生类通常是从多个基类派生出来的,这些基类之间可能毫无关系。派生类通常添加了它自己的数据成员和成员函数,因而通常比基类大的多。派生类比基类更具体,它代表了一组外延较小的对象。对于单重继承,派生类和基类有相同的起源。继承的真正魅力在于能够添加基类所没有的特点及
3、取代和改进从基类继承来的特点。,11、.为什么使用继承,第十一章:继承与类的派生,继承的目的在于为代码重用提供有效手段()可以重用先前项目的代码,如果原来的代码不满足要求,还可以做少量修改。()某个项目使用了几个非常相似或稍有不同的类,就可以通过派生类的继承性达到函数和数据继承的目的。继承最具有吸引力的特点是新类可以从现有的类库中继承,项目开发者可以开发出自己的类库。基于这种观点,将来有一天,软件也可以象当今的硬件一样用标准的可重用组件进行构造。,11、.派生类的定义,第十一章:继承与类的派生,派生类具有的特征:新的类可在基类所提供的基础上包含新的成员;在新的类中可隐藏基类的任何函数成员;可为
4、新的类重新定义函数。定义格式:Class派生类名 继承方式基类名 派生类新定义成员其中 继承方式常使用如下三种关键字给予表示:public表示公有基类private表示私有基类,可以省略protected表示保护基类,11、.派生类对基类成员的访问权,第十一章:继承与类的派生,派生类并不是对基类中的所有成员都可以无条件的进行访问对于基类的私有成员,派生类及派生类的使用者都无权访问。对于基类的公有成员的访问权,在定义派生类时,定义的方式不同,权限也不同。,11、.派生类对基类成员的访问权,第十一章:继承与类的派生,、私有派生,私有派生类对基类的公有成员只能是私有继承,也即基类的所有公有成员都只能
5、成为私有派生类的私有成员,这些私有成员只能被派生类的成员函数访问,而派生类的使用者无权访问。若希望基类中某些公有成员在派生类中也是公有的,使得派生类的使用者能够使用它,则可以在派生类的公有段中说明这些成员,并在成员名前缀上“类名”。有几个成员需要成为派生类的公有成员,在派生类中分别给出类似的定义。,举例见教材P169-170,11、.派生类对基类成员的访问权,第十一章:继承与类的派生,2、公有派生,公有派生的意义是基类中所有的公有成员在派生类中也都是公有的,这就省去了每个成员均要在派生类中声明才能变为公有的工作。基类的公有成员保持原有的状态。,举例见教材P170,11、.派生类对基类成员的访问
6、权,第十一章:继承与类的派生,、示意图,举例见教材P171-172-173,class base,private,public,class derive1:base,private,public,class derive2:public derive1,private,public,11、.派生类的构造函数和析构函数,第十一章:继承与类的派生,(1)派生类构造函数,我们已知道,派生类的对象的数据结构是由基类中说明的数据成员和派生类中说明的数据成员共同构成。将派生类的对象中由基类中说明的数据成员和操作所构成的封装体称为基类子对象,它由基类中的构造函数进行初始化。构造函数不能够被继承,因此,派生类
7、的构造函数必须通过调用基类的构造函数来初始化基类子对象。所以,在定义派生类的构造函数时除了对自己的数据成员进行初始化外,还必须负责调用基类构造函数使基类数据成员得以初始化。如果派生类中还有子对象时,还应包含对子对象初始化的构造函数。,11、.派生类的构造函数和析构函数,第十一章:继承与类的派生,(1)派生类构造函数,派生类构造函数的一般格式如下:():(参数表1),();,举例见教材P176,11、.派生类的构造函数和析构函数,第十一章:继承与类的派生,(1)派生类构造函数,在下面两种情况下,必须定义派生类的构造函数:派生类本身需要构造函数;定义派生类对象时,其相应的基类对象需调用带有参数的构
8、造函数。可以不定义派生类构造函数的情况:若基类使用缺省构造函数或不带参数的构造函数,在派生类定义构造函数时,可以省略基类“:构造函数(变元表)”;在此情况下,若派生类不需要初始化,则可以不定义派生类构造函数。,执行顺序是:先祖先(基类),再客人(对象成员),后自己(派生类),11、.派生类的构造函数和析构函数,第十一章:继承与类的派生,()派生类析构函数,在派生类中是否要定义析构函数与基类无关,各自独立执行。若基类、成员类、派生类均有析构函数,在执行时的顺序刚好与构造函数的顺序相反。即:派生类-成员类-基类,执行顺序是:先自己(派生类),再客人(对象成员),后祖先(基类),11、.派生类的构造
9、函数和析构函数,第十一章:继承与类的派生,()带有构造函数和析构函数的类的派生类举例,举例:【举例】(派生类与基类的构造函数和析构函数调用顺序),举例见教材P177-178,11、派生类对基类成员的继承,第十一章:继承与类的派生,焦点:如何来调整派生类的访问权限?,11、.如何访问基类私有成员,第十一章:继承与类的派生,不管私有派生还是公有派生,派生类无权访问基类私有成员,只有通过基类提供的公有函数(接口)进行访问。为达到直接访问可以采用以下两种方式:增加保护段声明友元,11、.如何访问基类私有成员,第十一章:继承与类的派生,、在类定义体中增加保护段,关键字:protected将基类私有成员中
10、需提供给派生类访问的部分定义为保护段成员。保护段成员可以被它的派生类访问,但是对于外界是隐藏起来的。即:就这个类的用户而言,它是private的,但它可被从这个类继承来的任何来使用。派生类对于保护段成员的继承与公有段成员的继承很相似,分以下两种情况:公有派生:基类的保护成员在派生类中处于保护成员;私有派生:基类的保护成员在派生类中处于私有成员;,举例见教材P184,11、.如何访问基类私有成员,第十一章:继承与类的派生,、将需要访问基类私有成员的派生类成员函数声明为基类的友元,基类的保护段成员可以通过一定的处理(连续的公有派生),使其成为对外界开放的。将待访问的派生类中的成员函数说明是基类的友
11、元,这样派生类中的其他成员函数都无权访问它,外界不可能通过派生新类来达到访问基类私有成员的目的。如果想让派生类中的所有成员函数均有权访问基类的私有成员,可以将整个派生类声明为基类的友元。,11、.通过访问声明调整访问域,第十一章:继承与类的派生,访问声明用来改变到派生类中变为私有的基类公有成员的访问域。它遵循以下规则:(1)访问声明仅仅调整名字的访问,不可以为它说明任何类型;若名字为成员函数,在访问声明时也不准说明任何参数;(2)访问声明只能调整基类的保护段和公有段成员在派生类中的访问域,不能改变基类的私有成员在派生中的访问域,任何试图这样的操作都是非法的;(3)访问声明仅用于在派生类中调整名
12、字的访问权限,不允许在派生类中降低或提升基类成员的可访问性。(4)对重载函数名的访问声明将调整基类中具有该名的所有函数的访问域;(处于不同段也不可以)(5)若在派生类中具有与基类中同名的函数,则基类中的此函数不允许在派生类中进行访问声明。,举例见教材P191-192-193,11、.不同继承方式的基类和派生类的特性,第十一章:继承与类的派生,11、.不同继承方式的基类和派生类的特性,第十一章:继承与类的派生,为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。对于公有继承方式:(1)基类成员对其对象的可见性:公有成员可见,其他不可见。这里保护成员同于私有成员
13、。(2)基类成员对派生类的可见性:公有成员和保护成员可见,而私有成员不可见。这里保护成员同于公有成员。(3)基类成员对派生类对象的可见性:公有成员可见,其他成员不可见。所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。这里,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。,11、.不同继承方式的基类和派生类的特性,第十一章:继承与类的派生,为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。对于私有继承方式:(1)基类成员对其对象的可见性:公有成员可见,其他成员不可见。(2)基类
14、成员对派生类的可见性:公有成员和保护成员是可见的,而私有成员是不可见的。(3)基类成员对派生类对象的可见性:所有成员都是不可见的。所以,在私有继承时,基类的成员只能由直接派生类访问,而无法再往下继承。,11、.不同继承方式的基类和派生类的特性,第十一章:继承与类的派生,为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。对于保护继承方式:这种继承方式与私有继承方式的情况相同。两者的区别仅在于对派生类的成员而言,对基类成员有不同的可见性。,11、多继承,第十一章:继承与类的派生,在解决实际问题时,某个类也许具有多个类所具有的特点,因此,引入多继承机制。,11、
15、.多继承的概念,第十一章:继承与类的派生,多继承可以看作是单继承的扩展。所谓多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个单继承。,11、.多继承的定义,第十一章:继承与类的派生,多继承下派生类的定义格式如下:class:,;其中,,是三种继承方式:public、private、protected之一。,注意:在多重继承中,公有派生和私有派生对于基类成员在派生类中的可访问性与单继承的规则相同。,举例见教材P195,11、.多继承的构造函数和析构函数,第十一章:继承与类的派生,在多继承的情况下,派生类的构造函数格式如下:():(),(),(),其中,中各个参数包含了其后的
16、各个分参数表。注意:多继承下派生类的构造函数与单继承下派生类构造函数相似,它必须同时负责该派生类所有基类构造函数的调用。同时,派生类的参数个数必须包含完成所有基类初始化所需的参数个数。,、构造函数的定义,举例见教材P197,11、.多继承的构造函数和析构函数,第十一章:继承与类的派生,、构造函数和析构函数的执行顺序,派生类构造函数执行顺序是先执行所有基类的构造函数,再执行派生类本身构造函数,处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的各基类顺序,与派生类构造函数中所定义的成员初始化列表的各项顺序无关。也就是说,执行基类构造函数的顺序取决于定义派生类时基类的顺序。可见,派生类
17、构造函数的成员初始化列表中各项顺序可以任意地排列。析构函数的执行顺序正好相反。,11、.虚基类,第十一章:继承与类的派生,、为什么要使用虚基类,理由:合并基类的副本,虚基类,virtual 其中,virtual是虚类的关键字。虚基类的说明是用在定义派生类时,写在派生类名的后面。一个基类,在定义它的派生类时,在作为某些派生类的虚基类的同时,又作为另一些派生类的非虚基类,这种情况是允许的。例如:class x:virtual public b class y:virtual public b class z:public b class aa:public x,public y,public z,
18、11、.虚基类,第十一章:继承与类的派生,、虚基类的概念,11、.虚基类,第十一章:继承与类的派生,、虚基类的初始化,虚基类的初始化与一般的多继承的初始化在语法上是一样的,但构造函数的调用次序不同,原则如下:虚基类的构造函数在非虚基类之前调用;若同一层次中包含多个虚基类,这些基类的构造函数按它们说明的次序调用。若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数。虚基类只允许定义不带参数的或带缺省参数的构造函数。,举例见教材P207,11、基类与派生类的关系,第十一章:继承与类的派生,任何一个类都可以派生出一个新类,派生类也可以再派生出新类,因此,基类和派生类是相对而言
19、的。基类与派生类之间的关系可以有如下几种描述:1.派生类是基类的具体化类的层次通常反映了客观世界中某种真实的模型。在这种情况下,不难看出:基类是对若干个派生类的抽象,而派生类是基类的具体化。基类抽取了它的派生类的公共特征,而派生类通过增加行为将抽象类变为某种有用的类型。2.派生类是基类定义的延续先定义一个抽象基类,该基类中有些操作并未实现。然后定义非抽象的派生类,实现抽象基类中定义的操作。例如,虚函数就属此类情况。这时,派生类是抽象的基类的实现,即可看成是基类定义的延续。这也是派生类的一种常用方法。,11、基类与派生类的关系,第十一章:继承与类的派生,任何一个类都可以派生出一个新类,派生类也可
20、以再派生出新类,因此,基类和派生类是相对而言的。基类与派生类之间的关系可以有如下几种描述:3.派生类是基类的组合在多继承时,一个派生类有多于一个的基类,这时派生类将是所有基类行为的组合。派生类将其本身与基类区别开来的方法是添加数据成员和成员函数。因此,继承的机制将使得在创建新类时,只需说明新类与已有类的区别,从而大量原有的程序代码都可以复用,所以有人称类是“可复用的软件构件”。,11、继承使用的注意事项,第十一章:继承与类的派生,对于程序而言,设计孤立的类是比较容易的,难的是正确设计基类及派生类。避免“不吃白不吃”。如果类和类毫不相关,不可以为了使的功能更多而让继承的功能和属性。如果在逻辑上是
21、的“一种”,并且的所有功能和属性对都有意义,则允许继承的功能和属性。避免“母鸡下鸭蛋”。若在逻辑上是的一部分,则不允许从派生,而是要用和其他东西组合(“整体部分”关系)成。例如头与眼、鼻、耳、口等的关系。,总结:,第十一章:继承与类的派生,继承的几种方式及特点类继承的构造和析构函数虚基类的涵义继承的应用,11、.派生类的构造函数和析构函数,第十一章:继承与类的派生,class B:public Apublic:B()b=0;cout类B的缺省构造函数.n;B(int i,int j,int k);B()cout类B的析构函数.n;void Print();private:int b;A aa;
22、,#include class Apublic:A()a=0;cout类A的缺省构造函数.n;A(int i)a=i;cout类A的构造函数.n;A()cout类A的析构函数.n;void Print()const coutA,;int Geta()reutrn a;private:int a;,B:B(int i,int j,int k):A(i),aa(j)b=k;cout类B的构造函数.n;void B:Print()A:Print();coutB,AA.GETA()ENDL;,void main()B bb2;bb0=B(1,2,5);bb1=B(3,4,7);for(int i=0;i2;i+)bbi.Print();,返回,