第4章继承一、基类和派生类二、派生类的构造函数和析构函数三、多重继承一、基类和派生类继承是面向对象程序设计的基本特征之一,是支持代码重用的重要机制。通过继承机制,在已有的类基础上建立新类。一个新类既可以共享另一个类的操作和数据,也可以在新类中定义已有类中没有的成员,这样就提供了无限重复利用程序资源的一种途径,大大的节省程序开发的时间和资源。C++中继承是类之间定义的一种重要关系。定义类B时,自动得到类A的操作和数据属性,只需定义类A中所没有的新成分就可完成在类B的定义,这样称类B继承了类A或类A派生了类B,这种机制称为继承。A是基类,B是派生类。派生类可以具有基类的特性,共享基类的成员函数,使用基类的数据成员,还可以定义自己的数据成员和成员函数。注意:在C++中下列成员不能继承:构造函数和析构函数、友元关系、重载的new和delete运算符在C++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承。继承使基类和派生类之间具有了层次关系。B1,B2的派生类(多继承)AB1B2C1C2C3A的派生类(单继承)C3的基类B1,B2的基类A的派生类C1,C2,C3的基类B1的派生类类之间的继承与派生关系1.单继承派生类的定义class派生类名:继承方式基类名{//派生类新定义成员};其中,继承方式是三种继承方式:public,private或protected之一。在派生类的头部明确列出其直接基类,间接基类则不用列出。classA{protected:intaMember;voidfuna(){}};classB:protectedA{protected:intbMember;voidfunb(){}};classC:privateB{protected:intcMember;};派生类不仅继承了直接基类的成员,同时也继承了间接基类的成员。2.继承方式基类的成员可以有public(公有)、protected(保护)和private(私有)三种访问属性,基类的自身成员函数可以对基类中任何一个其他成员进行访问,但是通过基类的对象,就只能访问该类的公有成员。类的继承方式有public(公有继承)、protected(保护继承)和private(私有继承)三种,不同的继承方式,导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。这里说的访问来自两个方面:一是派生类中的新增成员对从基类继承来的成员的访问;二是在派生类外部通过派生类的对象对从基类继承来的成员的访问。(1)公有继承(public)当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可访问,即基类的公有成员和保护成员被继承到派生类中仍作为派生类的公有成员和保护成员,派生类的其他成员可以直接访问它们。在派生类外部通过派生类的对象只能访问继承来的公有成员,而无论是派生类的成员还是派生类的对象都无法访问基类的私有成员。(2)私有继承(private)当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可访问。也就是说基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类外部通过派生类的对象无法访问。无论是派生类的成员还是通过派生类的对象,都无法访问基类的私有成员。经过私有继承之后,所有基类的成员都成为了派生类的私有成员或不可访问的成员,如果进一步派生的话,基类的全部成员就无法在新的派生类中被访问。因此,私有继承之后,基类的成员再也无法在以后的派生类中发挥作用,实际是相当于中止了基类功能的继续派生,出于这种原因,一般情况下私有继承的使用比较少。(3)保护继承(protected)保护继承中,基类的公有和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可访问。这样,派生类的其他成员就可以直接访问它们,但在类外部通过派生类的对象无法访问它们。无论是派生类的成员还是派生类的对象,都无法访问基类的私有成员。比较私有继承和保护继承,可以看出在直接派生类中,对所继承成员的访问是完全相同的。但是,如果派生类作为新的基类继续派生时,二者的就有区别。基类成员的访问权限继承方式派生类中的访问权限派生类之外能否访问在派生类的子类中能否访问publicpublicpublic可访问能protectedprotected不可访问publicprivateprivate不可访问不能protectedpublicprotectedprotected不可访问能protected派生类实际包含的成员可以按访问权限划分为四种:公有成员:是由派生类新声明的或基类的公有成员经公有派生而来的。保护成员:可以是由派生类新声明的成员、基类的保护成员公有派生的或基类的公有成员和保护成员经保护派生而来的。私有成员:是由派生类新声明的或基类的公有和保护成员经私有派生而来的。类中不可直接访问的成员:是由基类的私有成员派生而来的。3.在派生类中重定义基类的函数派生类自动继承了基类中定义的数据成员和成员函数。如果派生类认为基类中某个成员函数的实现不能满足需要,可以在派生类中重新定义该函数。重定义基类的成员函数需要使用和该函数相同的函数名和参数列表,如果参数列表不同,就是函数重载而不是函数的重定义了。经过重定义之后,派生类定义的函数取代了基类中原来的函数定义,也就是说,通过派生类对象访问到的该成员函数是在派生类中重新定义的版本,基类中定义的版本不能通过派生类对象访问。如果派生类的成员函数需要访问基类中被重定义的函数,可以使用基类名加作用域运算符::的方式。二、派生类的构造函数和析构函数派生类不能继承基类的构造函数和析构函数,但派生类的构造函数和析构函数与基类的构造函数和析构函数之间存在一种自动调用的关系。1.构造函数当实例化派生类的对象时,程序会自动调用派生类的构造函数,而派生类的构造函数在执行之前会先自动调用基类的构造函数,所以派生类的构造函数只需要初始化在派生类中定义的数据成员,而从基类继承的数据成员则交给基类的构造函数去初始化。派生类的构造函数调用基类的构造函数的方式有两种:显式调用:如果需要给基类的构造函数传递参数时,必须使用显式调用的方式,在参数列表之后用冒号(:)引出基类构造函数的函数名和参数。隐式调用:如果要调用的是基类的默认构造函数(不需要传递参数)则可以使用隐式调用,采用隐式调用时派生类构造函数的实现中不需要列出基类构造函数的函数名,系统会自动执行基类的默认构造函数来完成基类中数据的初始化。2.析构函数派生类的析构函数总是先执行自己的函数体,然后再自动调用基类的析构函数。其顺序与执行构造函数时的顺序正好相反。3.赋值兼容规则赋值兼容规则是指在公有派生条件下,任何可以使用基类对象的地方都可以用其派生类的对象替代。公有派生类的对象可以无条件地作为基类的对象处理。具体赋值规则如下:派生类的对象可以赋值给基类对象。派生类的对象可以初始化基类对象的引用。派生类对象的地址可以赋给指向基类对象的指针。例如:classA{...};classB:publicA{voidfun(){...}};Aa;Bb;a=b;//将对象b中所含类A成员的值赋给对象aA&a1=b;//用派生类对象初始化基类对象的引用A*pa=&b;//用派生类对象的地址初始化基类对象的指针注意:私有派生类对象或保护派生类对象不可以赋给基类对象、基类的引用或基类的指针。基类对象不能直接赋给派生类对象三、多继承在C+十中,一个派生类也可以从多个基类派生而来,这种继承方式称为多重继承。格式:Class派生类名:继承方式1基类名1,继承方式2基类名2,…{派生类类体};例:classA{…};classB{…};classC:publicA,privateB{…};//C公有继承A,私有继承B多重继承允许一个派生类同时继承多个基类中的成员,支持了软件的重用性,但也可能带来大量的二义性问题。复合与继承在软件渐增式开发中的应用面向对象的软件开发方法一般是从分析问题模型开始,然后识别出一个个对象,并对它们进行细化、抽象,最后用程序语言去描述它们的过程。这种软件开发方法从本质上来讲是迭代的和渐增的,其过程就是一次次的迭代反复的过程。随着迭代的进行,系统的功能不断完善。和传统的瀑布式软件开发模型相比,面向对象软件开发在分析、设计和编码等各个阶段之间的界限变得模糊起来。因为面向对象软件开发关心的是对象以及对象之间的关系。与结构化程序设计的思想相比,面向对象软件开发的重心从编码向分析偏移,从以功能为中心向以数据为中心偏移。而且,面向对象软件开发中的继承和复合使得软件的重用变得更加自然。对象之间通过继承实现了“是(is_a)”的关系,通过复合可实现把对象作为另外一个类的成员的“有(has_a)”的关系。“有”关系通过包含(复合)现有类的对象构造新类,例如圆柱类Column和圆类Circle,如果说圆柱就是一个圆显然不合适,但如果说圆柱中包含一个圆就合适了。“是”的关系则通过继承现有类(基类)中的成员来构造新类,如前面定义类People和Teacher,一个Teacher也可以作为一个People,对People具有的属性和操作也同样适合于Teacher。因此在已建立类People的前提下,可通过继承的方式建立类Teacher。无论复合还是继承,都能使得新的类包含或包容已有类的属性和操作。在程序设计时应该如何选择呢?如果在新的类中有成员是已有类的对象,使用复合方式比较合适;如果新的类可以视为已有类的子类或特例,新类的对象也可以作为已有类的对象使用,那么使用继承更合适。例:首先定义并实现了点类Point然后定义了一个线段类Line,Line以复合的方式使用了类Point,Line的表示端点的两个成员startPoint和endPoint都是Point的实例;定义了多边形类Polygon,Polygon使用Line,Polygon表示边的成员borders是一个Line对象数组,这也是一种复合方式;最后从类Polygon派生了子类Triangle,Triangle是从Polygon按照公有方式派生的。小结掌握:单继承的定义语法派生类对基类的访问规则单继承派生类的构造函数和析构函数了解:多继承的定义语法二义性问题