6.1继承与派生6.2单继承6.2.1公有派生6.2.2私有派生6.2.3保护派生6.3多继承6.4派生类的构造函数与析构函数6.5二义性问题6.6虚基类6.7赋值兼容原则第6章继承本章介绍继承的基本知识:单继承、多继承、公有派生、私有派生和保护派生;讨论派生类的构造函数、析构函数及多继承中的二义性问题;并详细讲解虚基类和赋值兼容原则。可以把继承过程看成是从一个类派生出一个新类的过程。派生出来的新类称为派生类或子类;而被继承的类称作基类或父类。一个基类可以派生出多个派生类,一个派生类也可以由多个基类派生而来。称从一个基类派生出一个派生类的过程为单继承;从多个基类派生出一个派生类的过程为多继承。【6.1继承与派生】继承是面向对象程序设计支持代码重用的重要机制。通过继承,可以在原有类的基础上派生出新类,新类将共享原有类的属性,并且还可以添加新的特性。计算机类台式机类便携式类输入设备类输出设备类硬盘类a)单继承b)多继承图单继承与多继承通过单继承派生一个类的一般形式如下:class派生类名:派生类型基类名{声明派生类新成员};派生类型用的关键字public:表示公有派生private:表示私有派生protected:表示保护派生在声明一个派生类时,采用不同的派生类型,从基类继承而来的成员的访问权限在派生类中也有所不同。是通过单继承派生出来的类的名称。用关键字public、private或protected指定,如果省略,系统将默认为私有派生。指明了这个派生类的基类,这个基类必须在声明这个派生类之前已经声明,否则,会导致编译错误。【6.2单继承】当采用公有派生时,基类中的公有(public)成员和保护(protected)成员的访问权限在派生类中保持不变,而基类的私有(private)成员无论是在派生类中,还是在类外都是不可访问的。例6-1公有派生#includeiostream.hclassA{public:voidsetx(inta){x=a;}voidsety(intb){y=b;}intgetx()const{returnx;}intgety()const{returny;}基类的公有成员和保护成员被派生类继承过来,作为派生类的公有成员和保护成员;但基类的私有成员在派生类中不能直接使用。简述【6.2.1公有派生】protected:intx;private:inty;};classB:publicA{public:intgetsum(){returnx+gety();}//直接访问从基类继承来的保护成员x;但是基//类的私有成员y,只能通过接口函数访问。};intmain(){Bb;b.setx(2);b.sety(3);cout″X=″b.getx()″\tY=″b.gety()endl;cout″X+Y=″b.getsum()endl;return0;}公有派生例6-1在公有派生时,基类和派生类中的公有成员都可以直接通过派生类的对象名来访问。一个类的保护成员只能被它自己的成员函数或它的派生类的成员函数访问,在类外不能直接访问。X=2Y=3X+Y=5当采用私有派生时,基类的私有(private)成员与公有派生时相同,无论是在派生类中,还是类外都是不可访问的。但基类中的公有(public)成员和保护(protected)成员的访问权限在派生类中则变为私有。基类的公有成员和保护成员被派生类继承过来,作为派生类的私有成员;而基类的私有成员在派生类中不能直接使用。简述【6.2.2私有派生】#includeiostream.hclassA{public:voidsetx(inta){x=a;}voidsety(intb){y=b;}intgetx()const{returnx;}intgety()const{returny;}protected:intx;private:inty;};classB:privateA{public:voidsetBx(inta){setx(a);}voidsetBy(intb){sety(b);}私有派生说明:类B是由类A私有派生而来的,类A中的公有和保护成员被类B继承过来后,作为私有成员,因此,无法直接通过类B的对象访问类A中的任一成员。例6-2intgetBx(){returngetx();}intgetBy(){returngety();}intgetsum(){returnx+gety();}};intmain(){Bb;b.setBx(2);b.setBy(3);cout″X=″b.getBx()″\tY=″b.getBy()endl;cout″X+Y=″b.getsum()endl;return0;}X=2Y=3X+Y=5(续)例6-3保护派生#includeiostream.hclassA{public:voidsetx(inta){x=a;}voidsety(intb){y=b;}intgetx()const{returnx;}intgety()const{returny;}当采用保护派生时,基类的私有(private)成员与公有派生时相同,无论是在派生类中,还是类外都是不可访问的。但基类中的公有(public)成员和保护(protected)成员的访问权限在派生类中则变为保护。基类的公有成员和保护成员被派生类继承过来,作为派生类的保护成员;而基类的私有成员在派生类中不能直接使用。简述【6.2.3保护派生】protected:intx;private:inty;};classB:protectedA{public:voidsetBx(inta){setx(a);}voidsetBy(intb){sety(b);}intgetBx(){returngetx();}intgetBy(){returngety();}intgetsum(){returnx+gety();}};intmain(){Bb;b.setBx(2);例6-3保护派生b.setBy(3);cout″X=″b.getBx()″\tY=″b.getBy()endl;cout″X+Y=″b.getsum()endl;return0;}引入保护成员的意义在于使类中的这些成员对该类及它的派生类是可见的,但在程序的其他部分与私有成员一样,是不可见的。在程序的任何部分如果声明有一个类的对象,就可以通过这个对象访问对象所属类中的所有公有成员,但不能访问其私有和保护成员;而一个派生类的成员函数则可以访问所属类中的新声明的所有成员,以及派生它的基类中的公有和保护成员。一个类的私有成员只能被所属类的成员函数访问,在其他任何地方都不可见。注X=2Y=3X+Y=5(续)派生类型基类成员特性派生类成员特性公有(public)公有(public)保护(protected)保护私有(private)不可访问公有(public)私有(private)保护(protected)私有(private)私有(private)不可访问公有(public)保护(protected)保护(protected)保护公有派生(public)私有派生(private)保护派生(protected)私有(private)不可访问表不同派生类型中相应基类及派生类的访问特性多继承可以看作是单继承的扩充,它是指由多个基类派生出一个类的情形。声明形式如下:class派生类名:派生类型1基类名1,派生类型2基类名2,…派生类型n基类名n{声明派生类新成员};每一个派生类型对应的是紧接其后给出的基类,而且必须给每个基类指定一种派生类型,如果缺省,相应的基类则取私有派生类型,而不是和前一个基类取相同的派生类型。【6.3多继承】多继承#includeiostream.hclassA{public:voidsetx(inta){x=a;}voidsety(intb){y=b;}比如:classA{...};classB{...};classC:publicA,B{...};说明:类A派生类C时,采用的是公有派生,也即类A中的公有和保护成员被类C继承过来且成员类型保持不变,但类C中的成员函数不能访问类A的私有成员。例6-4intgetx()const{returnx;}intgety()const{returny;}protected:intx;private:inty;};classB{public:voidsetz(intc){z=c;}intgetz()const{returnz;}protected:intz;};classC:publicA,privateB{public:voidsetCz(intc){setz(c);}intgetCz()const{returnz;}intgetsum()const{returnx+gety()+z;}(续)X=2Y=3Z=5X+Y+Z=10多继承可以看作是多个单继承的组合,单继承可以看作是多继承的特例。};intmain(){Cc;c.setx(2);c.sety(3);c.setCz(5);cout″X=″c.getx()″\tY=″c.gety()″\tZ=″c.getCz()endl;cout″X+Y+Z=″c.getsum()endl;return0;}(续)对于派生类,由于其中包含有从基类继承来的和派生类中新声明的数据成员,而派生类和它的基类都有相应的构造函数和析构函数。#includeiostream.hclassA{public:A();~A();voidsetx(inta){x=a;}voidsety(intb){y=b;}intgetx()const{returnx;}intgety()const{returny;}protected:intx;private:例6-5调用构造函数和析构函数【6.4派生类的构造函数与析构函数】例6-5inty;};classB:publicA{public:B();~B();voidsetz(intc){z=c;}intgetz(){returnz;}intgetsum(){returnx+gety()+z;}private:intz;};A::A():x(1),y(2){cout″调用类A的构造函数″endl;}A::~A()调用构造函数和析构函数{cout″调用类A的析构函数″endl;}B::B():z(3){cout″调用类B的构造函数″endl;}B::~B(){cout″调用类B的析构函数″endl;}intmain(){Bb;cout″X=″b.getx()″\tY=″b.gety()″\tZ=″b.getz()endl;cout″X+Y+Z=″b.getsum()endl;return0;}(续)通过为派生类定义一个带有初始化列表的构造函数。在创建派生类的对象时,系统将首先调用其基类的构造函数来初始化从基类中继承的数据成员,然后调用派生类自身的构造函数初始化在派生类中新声明的数据成员。终止对象时,析构函数的执行顺序则正好相反,即先调用派生类的析构函数清除派生类中新声明的数据成员,再调用基类的析构函数清除从基类中继承的数据成员。注调用类A的构造函数调用类B的构造函数X=1Y=2Z=3X+Y+Z=6调用类B的析构函数调用类A的析构函数(续)派生类名::派生类名(参数总表):基类1(参数表1),基类2(参数表2),…,基类n(参数表n),对象成员1(对象成员参数表1),对象成员2(对象成员参数表2),…,对象成员n(对象成员参数表n){派生类中新声明的数据成员初始化语句}声明一个多继承产生的派生类的对象时,派生类的基类构造函数之间有一个执行顺序问题。参数总表应包括初始化基类数据成员、内嵌对象数据成员及其他数据成员所需的全部数据。是需要用参数初始