第4章派生新类2019/12/1414.3.4派生类构造函数和析构函数通过前面的学习,我们知道派生类的对象自动拥有基类的所有数据成员,所以,在定义派生类的构造函数时除了对派生类的数据成员进行初始化外,还必须初始化基类的数据成员。如果派生类中有对象成员时,还要初始化对象成员。派生类构造函数的一般格式如下:派生类名(总参数表):基类名(参数表1),对象成员名(参数表2){派生类数据成员的初始化};其中,总参数表包含完成基类初始化所需的参数。第4章派生新类2019/12/142//rectangle.hclassPoint//baseclass{public:Point(floatxx=0,floatyy=0){X=xx;Y=yy;}voidMove(floatxOff,floatyOff){X+=xOff;Y+=yOff;}floatGetX(){returnX;}floatGetY(){returnY;}private:floatX,Y;};classRectangle:publicPoint//derivedclass{public:Rectangle(floatx,floaty,floatw,floath):Point(x,y){W=w;H=h;}floatGetH(){returnH;}floatGetW(){returnW;}private:floatW,H;};第4章派生新类2019/12/143//test.cpp#includerectangle.h#includeiostreamusingnamespacestd;intmain(){Rectanglerect(2,3,20,10);rect.Move(3,2);coutThedataofrect(X,Y,W,H):endl;coutrect.GetX(),rect.GetY(),rect.GetW(),rect.GetH()endl;return0;}Therunresultis:Thedataofrect(X,Y,W,H):5,5,20,10第4章派生新类2019/12/144注意作为一般规则,派生类构造函数最好不要直接向一个基类数据成员赋值,而是通过初始化列表把值传递给基类的适当的构造函数。当基类中没有显式定义构造函数时,派生类构造函数的初始化列表可以省略对基类的初始化。当基类的构造函数使用一个或多个参数时,派生类必须定义构造函数,将参数传递给基类的构造函数。第4章派生新类2019/12/145classPerson{public:Person(conststringName,intAge,charSex);stringGetName(){return(name);}intGetAge(){return(age);}charGetSex(){return(sex);}voidDisplay();protected://保护成员intage;private:stringname;charsex;};Person::Person(conststringName,intAge,charSex):name(Name){age=Age;sex=Sex;}voidPerson::Display(){coutname:name'\t';//直接访问本类私有成员coutage:age'\t';coutsex:sexendl;}第4章派生新类2019/12/146classStudent:publicPerson{public://外部接口Student(stringpName,intAge,charSex,stringpId,floatScore):id(pId),Person(pName,Age,Sex){score=Score;}stringGetId(){return(id);}floatGetScore(){returnscore;}voidDisplay();private:stringid;floatscore;};voidStudent::Display(){coutid:id'\t';coutage:age'\t';coutscore:scoreendl;}第4章派生新类2019/12/147intmain(){stringname;coutEnteraperson′sname:;cinname;Personp1(name,29,'m');//基类对象p1.Display();//基类对象访问基类公有成员函数coutEnterastudent′sname:;cinname;Students1(name,19,'f',03410101,95);//派生类对象coutname:s1.GetName()'\t';//访问基类成员函数coutid:s1.GetId()'\t';coutage:s1.GetAge()'\t';coutsex:s1.GetSex()'\t';coutscore:s1.GetScore()endl;s1.Display();//同名屏蔽s1.Person::Display();//显式访问基类公有成员函数return0;}第4章派生新类2019/12/148Student类由基类Person的成员和一个string类对象成员构成。在构建派生类对象时初始化顺序是:先是基类中的name对象成员,接着是基类的其它成员,然后是派生类中的id对象成员,最后是派生类的其它成员。尽管Person被放在初始化列表中的第二位,但总是先于id被初始化。构造函数执行顺序为:基类的构造函数→对象成员构造函数→派生类的构造函数。析构函数执行顺序刚好相反。Student类对象有两个Display(),这时可以用类名加作用域运算符来限定调用哪个Display()。如果不限定,则调用派生类的Display()。第4章派生新类2019/12/149现在可以来解决前面提出的问题了,如图所示。但是销售经理具有经理和销售员的特征,即由两个基类派生而来,这涉及多继承。第4章派生新类2019/12/1410Employee#name:string#no:int#salary:float#maxno:int=1000+Employee()+Pay():void+Display():voidTechnician-hourlyrate:float-worknours:int+Technician()+Pay():void+Display():voidSalesman#commrate:float#sales:float+Salesman()+Pay():void+Display():voidManager+Manager()+Pay():void+Display():void#monthlypay:float+SalesManager()+Pay():void+Display():voidSalesManager第4章派生新类2019/12/14114.4多继承与虚拟继承一个类可以从一个或者多个基类派生而来。根据派生类继承基类的个数,将继承分为单继承和多继承。当派生类有多个基类时称为多继承。单继承可以看作是多继承的一个特例,多继承可以看作是多个单继承的组合,它们有很多相同特性。第4章派生新类2019/12/1412多继承派生类的定义多继承派生类的定义格式如下class派生类名:继承方式基类名1,…,继承方式基类名n{派生类新定义成员};与单继承一样,默认继承方式为私有继承。第4章派生新类2019/12/1413多继承派生类的构造函数多继承派生类构造函数的定义格式如下派生类名(总参数表):基类名1(参数表1),…,基类名n(参数表n){派生类数据成员的初始化};其中,总参数表包含完成所有基类初始化所需的参数。第4章派生新类2019/12/1414多继承派生类的构造函数与单继承一样,要负责所有基类的初始化工作。构造函数执行顺序是:先执行所有基类的构造函数,再执行对象成员类构造函数,最后执行派生类的构造函数。处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的基类顺序,与派生类构造函数中所定义的成员初始化列表顺序无关。析构函数执行顺序与构造函数的执行顺序相反。第4章派生新类2019/12/1415示例多继承构造函数和析构函数执行顺序classA//定义基类A{public:A(inti){a=i;coutAConstructorendl;}voiddisp(){couta=aendl;}~A(){coutADestructorendl;}private:inta;};classB//定义基类B{public:B(intj){b=j;coutBConstructorendl;}voiddisp(){coutb=bendl;}~B(){coutBDestructorendl;}private:intb;};第4章派生新类2019/12/1416classC:publicB,publicA//定义派生类C。B在前,A在后{public:C(intk):A(k+2),B(k-2)//包含基类成员初始化列表{c=k;coutCConstructorendl;}voiddisp(){A::disp();//用类名加作用域运算符限定B::disp();coutc=cendl;}~C(){coutCDestructorendl;}private:intc;};intmain(){Cobj(10);obj.disp();//调用类C的成员函数dispreturn0;}第4章派生新类2019/12/1417提出问题在现实问题中,存在这样的情况:那么C类从B1和B2类继承下来两份A类的成员,在使用时出现二义性,如何解决?A类B1类B2类C类第4章派生新类2019/12/1418Employee#name:string#no:int#salary:float#maxno:int=1000+Employee()+Pay():void+Display():voidTechnician-hourlyrate:float-worknours:int+Technician()+Pay():void+Display():voidSalesman#commrate:float#sales:float+Salesman()+Pay():void+Display():voidManager+Manager()+Pay():void+Display():void#monthlypay:float+SalesManager()+Pay():void+Display():voidSalesManager第4章派生新类2019/12/1419解决方案一用作用域运算符“::”进行限定,显式访问基类成员。例如:派生类C的对象访问基类的disp函数时用“A::disp()”和“B::disp()”显式访问。第4章派生新类2019/12/1420解决方案二为避免对基类成员访问的二义性问题,可以将派生类的直接基类(如Salesman、Manager)的共同基类(如Employee)设置为虚基类,这样共同基类(Employee)在内存中只有一个副本存在。即将Employee类设为虚基类!第4章派生新类2019/12/1421虚基类虚基类的定义格式为:class派生类名:virtual继承方式共同基类名;注:引进虚基类后,派生类对象中只存在一个虚基类成员的副本。第4章派生新类2019/12/1422示例虚基类c