第四章继承继承也叫派生。由一个类派生出一个新的类。用来派生的类叫基类(父类)。派生出的类叫派生类(子类)。基类派生类class类名{[public/protected/private]:数据成员;成员函数;友元声明;};class类名:继承方式public/protected/private基类名1,继承方式基类名2{[public/protected/private]:数据成员;成员函数;友元声明;};依据基类的个数,可以把继承分为单继承(一个基类)和多继承(多个基类)派生四部曲:1、接收基类成员。2、发展新成员3、改造基类成员4、书写自己的构造函数和析构函数。内容的展开:1、继承方式:public/protected/private2、不同继承方式下,派生类中,基类成员的访问权限?。基类成员原本有个定义的访问权限。基类定义的访问权限(类内/外)派生类继承方式基类成员在派生类中的访问权限(类内/外)特性public√√publicpublic√√保持protected√Xprotected√Xprivate√X不可访问XXpublicprotectedprotected√X保护protectedprotected√Xprivate不可访问XXpublicprivateprivate√X私有protectedprivate√Xprivate不可访问XX所有基类的私有成员,在派生类中都不可访问。总结:基类成员在派生类中的访问权限,取决于两大因素:基类自己定义的访问权和派生方式。依据派生方式,6字特征:公有继承保持,保护继承保护,私有继承私有。基类的私有成员,在派生类中都不可访问。3、派生四部曲。3.1接收基类成员。A.访问权的变化(基类成员,被派生类继承(继承方式3种)后,派生类对基类成员的访问权有变化)。B.不能接收什么成员?基类的构造函数和析构函数。基类的友元函数。基类的静态成员(数据/函数)3.2发展新成员。(规则同第三章)新成员能访问继承来的基类成员(除了私有及不可继承的成员)。3.3改造基类成员。A.为什么要改造?因为继承中会有些问题出现。二义性。(1)多继承中,多个基类中出现同名成员函数。classA{public:voidf(){cout“A\n”;}};classB{public:voidf(){cout“B\n”;}};classC:publicA,publicB{….….};voidmain(){Cc1;c1.f();//二义性}解决策略:1)加上(类名::)c1.A::f();2)同名覆盖(重定义)(派生类的同名函数可以覆盖基类的同名函数)classC:publicA,publicB{public:voidf(){A::f();B::f();cout”C\n”;}};2)多级派生下,共同基类的数据存在多个版本的形式(数据成员的二义性)。#includeiostream.hclassA{inta;public:voidseta(intaa){a=aa;}voidputa(){coutaendl;}};classB:publicA{intb;public:voidsetb(intbb){b=bb;}voidputb(){coutbendl;}};classC:publicA{intc;public:voidsetc(intcc){c=cc;}voidputc(){coutcendl;}};classD:publicB,publicC{intd;public:voidsetd(intdd){d=dd;}voidputb(){coutdendl;}};voidmain(){Dd1;d1.B::seta(10);d1.B::puta();d1.C::seta(47);d1.C::puta();}怎么整合到一个版本?(让共同基类的数据只继承一次。)使用虚基类。一般的继承是拷贝继承。虚基类是一种指针继承机制,所有的派生类并没拷贝数据,而是保留了该数据的存储地址。如何定义虚基类?首先弄清楚两个概念:直接派生类和最后派生类。直接派生类(儿子类):直接从需要定义为基类的类中派生的子类。最后派生类:在类结构中,最后用于定义对象的那个类,成为最后派生类。最后派生类只能获得虚基类的唯一一个版本数据。定义虚基类,必须使它所有的直接派生类中,都virtual某继承方式该基类。#includeiostream.hclassA//虚基类{inta;public:voidseta(intaa){a=aa;}voidputa(){coutaendl;}};classB:virtualpublicA//A类的直接派生类B{intb;public:voidsetb(intbb){b=bb;}voidputb(){coutbendl;}};classC:virtualpublicA//A类的直接派生类C{intc;public:voidsetc(intcc){c=cc;}voidputc(){coutcendl;}};classD:publicB,publicC{intd;public:voidsetd(intdd){d=dd;}voidputb(){coutdendl;}};voidmain(){Dd1;//D是最后派生类d1.B::seta(10);d1.B::puta();d1.C::seta(47);d1.C::puta();}通过定义虚基类来消除数据成员的二义性。3.4书写自己的构造函数和析构函数。若无基类,一个一般类的构造函数和析构函数只需要负责自己的数据的初始化和撤销。当作为派生类时,其析构函数和构造函数必须兼顾及基类的构造函数和析构函数。(原因是,基类的构造函数和析构函数不能被派生类继承)。基类的构造函数派生类的构造函数classA{A(){……..}//无参classB:publicA{B(参数表B){…….}//此时派生类的构造函数只需要负责自己的新成员的初始化即可,无需负责基类的成员初始化。A(参数表A){…….}//有参B(参数表B,参数表A):A(参数表A){…….}//此时派生类的构造函数的参数表,必须包含基类A的参数表,同时,必须在参数对照表中,直接显式的调用A的有参构造函数,并将参数表A作为其中的参数。};派生类构造函数的书写规则:总结:派生类的构造函数的结构,必须同其基类(只对父类负责)的构造函数的定义相对应,基类无参,派生只负责自己的初始化,基类有参,派生类中必须有对应格式的构造函数相对应。(子类只需负责其父类)若有虚基类,一旦声明为虚基类,则在其后的派生中,它都是虚基类。所有的派生子类都必须包含对其虚基类的构造函数的初始化。(所有子类都必须负责虚基类)原因:虚基类保证了最后派生类只继承它一次,因而每个子类都有可能成为最后派生类,都必须有负责虚基类构造函数的调用。若有对象成员。(每个类负责自己的对象成员的初始化)若存在这样一个类,它有多个父类及虚基类,还有用对象成员,那个这些类和对象的构造函数的调用顺序是怎样的?(派生类的构造函数必须兼顾其虚基类、一般基类和对象成员的构造函数。)(1)先调用虚基类的构造函数(只继承一次,也只调用一次)。(若有多个,依据定义顺序先后调用)(2)调用其一般基类的构造函数,顺序依据定义顺序。(3)调用其对象成员的构造函数。(4)派生类自己的构造函数。那么派生类的析构函数的析构顺序呢?和上面的顺序相反:即先派生类自己的析构函数,对象成员的析构函数、一般基类的析构函数,最后才是虚基类的析构函数调用。如何书写:1)注意其有无虚基类和基类,它们各自的构造函数的结构(有参/无参)。有参,则在构造函数书写中,必须负责其初始化,无参则无需注意。凡虚基类有有参构造函数,则其派生出的子孙都需负责其初始化。2)派生自己有无需要初始化的数据,(包括对象成员)。3)派生类的构造函数的参数表,必须包含其全部虚基类、基类、数据成员(包括对象成员)的数据。然后在参数表后,跟上“:”,跟上参数对照表,瓜分这些参数信息。派生类名(参数总表):虚基类名(参数表1),基类名(参数表2),…….对象成员(参数表N){其他数据成员初始化。}目的:保证所有的虚基类、基类、对象成员能够正常的初始化(有适合的构造函数可调用)。4)这些构造函数的调用顺序:调用的次序:先虚基类-----一般基类------对象成员------派生类自己。同级的构造函数的调用顺序取决于派生类定义的顺序。析构函数的调用顺序和这个相反,即:先派生类自己-----对象成员------一般基类------虚基类。#includeiostream#includestdio.h#includestringusingnamespacestd;//*********************classTime{inthour,min,sec;public:Time(){hour=0;min=0;sec=0;}Time(inth,intm,ints){hour=h;min=m;sec=s;}voidsettime(inth,intm,ints){hour=h;min=m;sec=s;}voidshowtime(void){if(hour12)cout(hour-12):min:secpm;elseif(hour=0)couthour:min:secam;}};classboom:publicTime{private:stringcolor;floatweight;public:boom(stringcol,floatweig){color=col;weight=weig;}boom(stringcol,floatweig,inth,intm,ints):Time(h,m,s){color=col;weight=weig;}voidsetcolor(stringcol){color=col;}voidsetweight(floatweig){weight=weig;}stringgetcolor(){returncolor;}floatgetweight(){returnweight;}voidboomit(){coutboom!!!!!endl;showtime();coutendl炸弹的信息:endl;cout颜色:color重量:weightendl;}voidshowtime(void){cout当前时间:;Time::showtime();}};//=====================intmain(){inth,m,s;cinhms;boomboom1(白色,5.8,h,m,s);boom1.showtime();cout输入爆炸时间:;cinhms;boom1.settime(h,m,s);boom1.boomit();return0;}赋值相容关系:在公有继承下,基类使用派生类对象来访问自己的成员。(使用方式:基类对象、引用、指针)(对将来学到的虚函数,很有用)#includeiostream.hclassA{intx;public:voidsetx(intxx){x=xx;}voidprintx(){coutxendl;}intgetx(){returnx;}};classB:publicA{inty;public:voidsety(intyy){y=yy;setx(yy);}voidprintxy(){cout\getx(),y\endl;}};voidmain(){Bb1,*pb;pb=&b1;pb-sety(10);pb-printxy();A&p=b1;p.setx(20);p.printx();}