【文档说明】[工学]程序设计与问题求解Ⅱ.ppt,共(69)页,873.617 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-2102.html
以下为本文档部分文字说明:
程序设计与问题求解Ⅱ第5章继承与派生2012.2.21程序设计与问题求解Ⅱ2本章主要内容类的继承与派生派生类派生类的继承方式与访问属性派生类的构造函数和析构函数多继承赋值兼容原则程序设计与问题求解Ⅱ3继承与派生问题客观世界中概念的层次结构交通工具汽车小汽车大卡车大客车面包
车轿车越野车火车飞机轮船视角:从下往上看,保持已有的特性——继承视角:从上往下看,增加了新的特性——派生继承派生程序设计与问题求解Ⅱ4继承与派生问题举例(1)圆矩形几何形状程序设计与问题求解Ⅱ5继承与派生问题举例(2)猴子狮子虎猎豹猫鸟
动物程序设计与问题求解Ⅱ6类的继承与派生(1)类的层次结构类的继承:一个新类从原有的类那里获得其已有的特性类的派生:从已有的类产生一个新的类基类(父类):派生新类的类派生类(子类):从基类派生而成的类学生类研究生基类派生类2派生类1中学生大学生
小学生高中生初中生硕士生博士生程序设计与问题求解Ⅱ7类的继承与派生(2)基类和派生类:构成类的层次关系单派生:派生一个子类的类多派生:派生多个子类的类学生类研究生基类派生类2派生类1中学生大学生小学生高中生初中生硕士生博士生例如上图:单派生:大学生派生研究生。多派生
:除了一个单派生其余的都是多派生。多派生程序设计与问题求解Ⅱ8类的继承与派生(3)基类和派生类:构成类的层次关系单继承:仅从一个基类派生而成的类多继承:从多个基类派生而成的类派生类在职研究生教师职员研究生在职人员基类2基类1在面向对象程序设计中
使用继承和派生有什么好处?如何使用?多继承例如上图:单继承:教师和职员都是单继承,只有一个父类。多继承:在职研究生就是多继承,有两个父类。程序设计与问题求解Ⅱ9类的继承与派生(4)classPerson//
人的基本信息{charName[20];charSex;intAge;public:voidRegister(char*name,charsex,intage){strcpy(Name,name);Sex=(sex=
='m'?'m':'f');Age=age;}voidShowMe(){cout<<Name<<'\t'<<Sex<<'\t'<<Age<<endl;}};程序设计与问题求解Ⅱ10类的继承与派生(5)classStudent//学生类{charName[2
0];charSex;intAge;intNumber;//学号charClassName[10];//班级public:voidRegisterStu(char*name,intsex,charage,intnumber,char*classname){strcpy(N
ame,name);Sex=(sex=='m'?'m':'f');Age=age;Number=number;strcpy(ClassName,classname);}voidShowStu(){cout<<Name<<'\t'<<Sex<<'\t'<<Age<<
endl;cout<<Number<<'\t'<<ClassName<<endl;}};从这个例子我们能发现什么?能否利用已有的代码?程序设计与问题求解Ⅱ11类的继承与派生(6)classStudent:publicPerson//公有继承{intNum
ber;//学号charClassName[10];//班级public:voidRegisterStu(char*name,intsex,charage,intnumber,char*classname){Register(name,sex,age);//直接调用基类的公有成员Number=
number;strcpy(ClassName,classname);}voidShowStu(){ShowMe();//直接调用基类的公有成员cout<<Number<<'\t'<<ClassName<<endl;}};继承与派生:Student是派生类,Person是基类继承已有的特
性,派生新的特性程序设计与问题求解Ⅱ12继承的目的:保持已有的特性,实现代码重用。派生的目的:增加新的特性,适应新的需要。类的继承:保留已有的数据成员和成员函数类的派生:增加新的数据成员和成员函数类的继承与派生(7)在
面向对象程序设计中如何使用继承和派生?程序设计与问题求解Ⅱ13派生类的定义格式class派生类名:继承方式基类名{新增成员声明;};继承方式公有继承(public)私有继承(private)保护继承(protected)派生类定义程序设计与问题求解Ⅱ14派生类对象
结构classA{inta,b;};classB:publicA{intc;};基类A对象子类B对象abcab基类部分子类增加部分派生类对象中总是含有基类对象(即含有基类的数据成员),其空间总是不小于基类对象。程序设计与问题求解Ⅱ15类的成员访问
属性有三种:public(公有)、private(私有)和protected(保护)。在类内,非静态成员函数可以访问类中的所有成员。在类外,通过类的“对象.成员”方式只能访问该类的公有成员。类的继承方式有三种:public(公有继承)、private(私有继承)和prot
ected(保护继承)。在派生类中继承来的成员访问属性是什么?继承来的成员是不是都可以直接访问?派生类的继承方式和访问属性(1)程序设计与问题求解Ⅱ16基类成员的访问属性继承方式publicprotectedprivatepublic在派生类中为public在派生类中为protec
ted在派生类中为private派生类的成员函数和类的作用域之外,都可以直接访问派生类的成员函数可以直接访问派生类的成员函数可以直接访问protected在派生类中为protected在派生类中为protected在派生类中为p
rivate派生类的成员函数可以直接访问派生类的成员函数可以直接访问派生类的成员函数可以直接访问private在派生类中被隔离,不可以直接访问在派生类中被隔离,不可以直接访问在派生类中被隔离,不可以直接访问任何方式都不能直接访问,但可以通过基类的public、protected成员函数间接访问任何
方式都不能直接访问,但可以通过基类的public、protected成员函数间接访问任何方式都不能直接访问,但可以通过基类的public、protected成员函数间接访问继承方式下的访问权限程序设计与问题求解Ⅱ17派生类的继承方式和访问属性(2)public(公有继承)classPer
son{charName[20];charSex;intAge;public:voidRegister(char*name,charsex,intage){strcpy(Name,name);Sex=(sex=='m'?
'm':'f');Age=age;}voidShowMe(){cout<<Name<<'\t'<<Sex<<'\t'<<Age<<endl;}};classStudent:publicPerson//公有继承{intNumber;//学号charClassName[10];//班级public:v
oidRegisterStu(char*name,intsex,charage,intnumber,char*classname){Register(name,sex,age);Age=age;Number=number;strcp
y(ClassName,classname);}voidShowStu(){ShowMe();cout<<Number<<'\t'<<ClassName<<endl;}};×√√程序设计与问题求解Ⅱ1
8派生类的继承方式和访问属性(续)public(公有继承)intmain(){Studentstu;//定义一个对象stu.RegisterStu("张弓长",'m',18,85071011,"计算机51");//本类中的公有成员函数stu.Age=18;//基类
的私有数据成员stu.Number=110012315;//本类中的私有数据成员stu.ShowStu();//本类中的公有成员函数stu.ShowMe();//基类的,成为派生类的公有成员函数return0;}×√√×√程序设计与问题求解Ⅱ19派生类的继承方式和访问属性(续
)public(公有继承)classPerson{protected:charName[20];charSex;intAge;public:voidRegister(char*name,charsex,intage){strcpy
(Name,name);Sex=(sex=='m'?'m':'f');Age=age;}voidShowMe(){cout<<Name<<'\t'<<Sex<<'\t'<<Age<<endl;}};classStud
ent:publicPerson//公有继承{intNumber;//学号charClassName[10];//班级public:voidRegisterStu(char*name,intsex,charage,intnumber,char*classname){Register(name,s
ex,age);Age=age;Number=number;strcpy(ClassName,classname);}voidShowStu(){ShowMe();cout<<Number<<'\t'
<<ClassName<<endl;}};√√√程序设计与问题求解Ⅱ20派生类的继承方式和访问属性(3)protected(保护继承)classPerson{charName[20];charSex;intAge;public:voidRe
gister(char*name,charsex,intage){strcpy(Name,name);Sex=(sex=='m'?'m':'f');Age=age;}voidShowMe(){cout<<Name<<'\t'<<Sex<<'\t'<<Age<<endl;}};classSt
udent:protectedPerson//保护继承{intNumber;//学号charClassName[10];//班级public:voidRegisterStu(char*name,intsex,charage,intnumber,char*classname){Re
gister(name,sex,age);Age=age;Number=number;strcpy(ClassName,classname);}voidShowStu(){ShowMe();cout<<Number<
<'\t'<<ClassName<<endl;}};×√√程序设计与问题求解Ⅱ21派生类的继承方式和访问属性(续)protected(保护继承)intmain(){Studentstu;stu.RegisterStu(
"张弓长",'m',18,85071011,"计算机51");stu.Age=18;//基类的私有数据成员stu.ShowStu();stu.ShowMe();//成为派生类的保护成员return0;}××程
序设计与问题求解Ⅱ22派生类的继承方式和访问属性(续)protected(保护继承)classPerson{protected:charName[20];charSex;intAge;public:v
oidRegister(char*name,charsex,intage){strcpy(Name,name);Sex=(sex=='m'?'m':'f');Age=age;}voidShowMe(){cout<<Name<<'\t'<<Sex<<'\t'<<Age<
<endl;}};classStudent:protectedPerson//保护继承{intNumber;//学号charClassName[10];//班级public:voidRegisterStu(char*nam
e,intsex,charage,intnumber,char*classname){Register(name,sex,age);Age=age;Number=number;strcpy(ClassName,classname);}void
ShowStu(){ShowMe();cout<<Number<<'\t'<<ClassName<<endl;}};√√√程序设计与问题求解Ⅱ23派生类的继承方式和访问属性(4)private(私有继承)classPerson{cha
rName[20];charSex;intAge;public:voidRegister(char*name,charsex,intage){strcpy(Name,name);Sex=(sex=='m'?'m':'f');Age=age;}voidShowMe(){cout<<Name<<'\t
'<<Sex<<'\t'<<Age<<endl;}};classStudent:privatePerson//私有继承{intNumber;//学号charClassName[10];//班级publ
ic:voidRegisterStu(char*name,intsex,charage,intnumber,char*classname){Register(name,sex,age);Age=age;Number=number;
strcpy(ClassName,classname);}voidShowStu(){ShowMe();cout<<Number<<'\t'<<ClassName<<endl;}};×√√程序设计与问题求解Ⅱ24派生类的继承方式和访问属性(续)pri
vate(私有继承)intmain(){Studentstu;stu.RegisterStu("张弓长",'m',18,85071011,"计算机51");stu.Age=18;stu.ShowStu();stu.Sh
owMe();//成为派生类的私有成员return0;}××程序设计与问题求解Ⅱ25在派生类中不管什么继承方式,只能访问父类的公有成员和保护成员,不能访问父类的私有成员。在派生类外,通过派生类对象不管什
么继承方式,只能访问公有成员(包括新增的和继承的),不能访问保护成员和私有成员(包括新增的和继承的)。小结程序设计与问题求解Ⅱ26classBase{private:inta;voidf(){cout<<a;}protected:intb;voidg(){cou
t<<b;}public:intc;voidk(){cout<<c;}};classSub:publicBase{public:voidtest(){a=1;f();b=2;g();c=3;k();}};voidm
ain(){Subd;d.a=1;d.f();d.b=2;d.g();d.c=3;d.k();}练习1√××√√√××××√√程序设计与问题求解Ⅱ27classBase{private:inta;voidf(){cout<<a
;}protected:intb;voidg(){cout<<b;}public:intc;voidk(){cout<<c;}};classSub:protectedBase{public:voidtest(){a=1
;f();b=2;g();c=3;k();}};voidmain(){Subd;d.a=1;d.f();d.b=2;d.g();d.c=3;d.k();}练习2√××√√√××××××程序设计与问题求解Ⅱ28cla
ssBase{private:inta;voidf(){cout<<a;}protected:intb;voidg(){cout<<b;}public:intc;voidk(){cout<<c;}};classSub:privateBase{public:voidtest(){a=1;f();b
=2;g();c=3;k();}};intmain(){Subd;d.a=1;d.f();d.b=2;d.g();d.c=3;d.k();}练习3protected继承与private继承的区别?√××√√√××××××程序设计与问题求解Ⅱ29classA{intmyPrivate;protect
ed:intmyProtected;public:intmyPublic;};classB1:protectedA{voidSetNum(){myProtected=1;myPublic=1;}};classC1:publicB1{voidSetNum(){myProtected=1;myP
ublic=1;}};classB2:privateA{voidSetNum(){myProtected=1;myPublic=1;}};classC2:publicB2{voidSetNum(){myProtected=1;myPubl
ic=1;}};多层派生(1)√√√×AB1C1B2C2程序设计与问题求解Ⅱ30不管有多少层派生,对某一个派生类来说派生类中继承成员的访问属性:只由其在直接父类的访问属性和继承方式确定。派生类中新增成员的访问属
性:由其在类中的定义确定。多层派生(2)程序设计与问题求解Ⅱ31基类的构造函数和析构函数都不能被继承,派生类中需要声明自己的构造函数和析构函数。在设计派生类的构造函数时候,不仅要考虑派生类所新增的数据成
员初始化,还要考虑基类的数据成员初始化。声明构造函数时,除了对本类中新增成员进行初始化外,对继承来的基类成员的初始化,需用初始化列表调用基类构造函数完成。派生类的构造函数(1)程序设计与问题求解Ⅱ32派生类构造函数
的一般形式派生类名(总参数表):基类名1(参数表1),基类名2(参数表2)…,对象成员名1(对象参数表1),对象成员名2(对象参数表2),…{派生类中新增数据成员的初始化}有三种数据要考虑初始化:基类中的数据成员初始化——
基类构造函数本类中的对象成员初始化——相应类的构造函数本类中新增的数据成员——函数体派生类的构造函数(2)程序设计与问题求解Ⅱ33派生类构造函数说明:总参数表:包括派生类中新增加的数据成员,以及全部基类和
全部对象成员的所有参数。如果基类中没有定义构造函数,或定义了没有参数的构造函数(默认构造函数),则在初始化列表中可略去“基类名(参数表)”。此时,若派生类及对象成员都不需初始化,则可以不定义派生类的构造函数
。如果基类中定义的构造函数带参数,则必须定义派生类构造函数,并通过初始化列表传递参数。如果基类中既定义无参的构造函数,又定义了有参的构造函数,在定义派生类构造函数时,既可以包含基类构造函数及其参数,也可以不包含基类构
造函数。派生类的构造函数(3)程序设计与问题求解Ⅱ34派生类的构造函数(4)派生类默认构造函数如果没有定义构造函数,则会调用默认的无参构造函数。派生类的默认构造函数首先会调用父类的无参构造函数(如果父类定义了有参构造函数,又没有
重载无参构造函数,则编译错误)。如果父类的上面还有父类,则依次类推。程序设计与问题求解Ⅱ35派生类构造函数执行次序:调用基类构造函数,调用顺序按照被声明时的顺序调用对象成员构造函数,调用顺序按照被声明时的顺序执行派生类构造函数体
派生类的构造函数(5)程序设计与问题求解Ⅱ36析构函数也不被继承,派生类需自行声明。声明方法与一般(无继承关系时)类的析构函数相同。不需要显式地调用基类的析构函数,系统会自动隐式调用。析构函数的调用次序与构造函
数相反。派生类的析构函数调用构造函数的次序:基类对象成员派生类基类对象成员派生类调用析构函数的次序:程序设计与问题求解Ⅱ37#include<iostream.h>#include<cstring>classPerso
n{charName[10];//姓名intAge;//年龄public:Person(char*name,intage){strcpy(Name,name);Age=age;cout<<"constructorofperson"<<Name<<endl;}~Per
son(){cout<<"deconstrutorofperson"<<Name<<endl;}};classStudent:publicPerson{charClassName[10];//班级PersonMonitor;//班长,对象成员public:Student(char*name,
intage,char*classname,char*name1,intage1):Person(name,age),Monitor(name1,age1){strcpy(ClassName,classname
);cout<<"constructorofStudent"<<endl;}~Student(){cout<<"deconstrucorofStudent"<<endl;}};intmain(){Studentstu("张弓长",18,"计算机51","李木子",20);return0;}程序
设计与问题求解Ⅱ38#include<iostream>#include<string>usingnamespacestd;classStudent{public:Student(intn,stringnam){num=n;name=nam
;}voiddisplay(){cout<<"num:"<<num<<endl;cout<<"name:"<<name<<endl;}protected:intnum;stringname;};classStudent1:publicStudent{public:Student1
(intn,stringnam,intn1,stringnam1,inta,stringad):Student(n,nam),monitor(n1,nam1){age=a;addr=ad;}voidshow(){cout<<"T
hisstudentis:"<<endl;display();cout<<"age:"<<age<<endl;cout<<"address:"<<addr<<endl;}voidshow_monitor(){cout<<endl<<"Classmonitori
s:"<<endl;monitor.display();}private:Studentmonitor;intage;stringaddr;};intmain(){Student1stud1(10010,"Wang-li",1001,"Liu-xiang",19,"115Beiji
ngRoad,Shanghai");stud1.show();stud1.show_monitor();return0;}程序设计与问题求解Ⅱ39多层派生时的构造函数#include<iostream>#include<string>u
singnamespacestd;classStudent{public:Student(intn,stringnam){num=n;name=nam;cout<<"constructorofStudent";cout<<"num:"<<num<<"name:"<<name<<endl
;}protected:intnum;stringname;};classStudent1:publicStudent{public:Student1(intn,stringnam,inta):Student(n,nam){age=a;cou
t<<"constructorofStudent1"<<"age:"<<age<<endl;}private:intage;};classStudent2:publicStudent1{public:Student2(intn
,stringnam,inta,ints):Student1(n,nam,a){score=s;cout<<"constructorofStudent2"<<"score:"<<score<<endl;}private:intscore;};intmain(){Student2stud(100
10,"Li",17,89);return0;}程序设计与问题求解Ⅱ40多层派生时的构造函数(续)先初始化Student的数据成员num和name再初始化Student1的数据成员age最后再初始化S
tudent2的数据成员score有多层派生时,每一层派生类的构造函数,不需要列出其上面所有各层派生类的构造函数,只需写出直接父类的构造函数即可。程序设计与问题求解Ⅱ41派生类的拷贝构造函数拷贝构造与构造函数的方式类似:派生类若自定义拷贝构造函数,则派生类对象在拷贝创建时先调用
父类的拷贝构造函数,再调用派生类拷贝构造函数完成自己的拷贝。若基类没有定义拷贝构造函数,则派生类对象在拷贝创建中调用基类默认的拷贝构造函数。派生类若没有定义拷贝构造函数,则派生类对象在拷贝创建时先调用父类的默认拷贝构造函数,再
调用派生类默认拷贝构造函数。程序设计与问题求解Ⅱ42继承与派生结构:单派生:一个基类派生出一个派生类多派生:一个基类派生出多个不同的派生类多层派生:派生类又作为基类,继续派生新的类单继承:派生类只从一个基类派生多(重)继承:派生类从多个基
类派生继承与派生结构程序设计与问题求解Ⅱ43多派生#include<iostream>#include<cstring>usingnamespacestd;classPerson{protected:charName[10];charSex;intAge;public:voidRegi
ster(char*name,intage,charsex){strcpy(Name,name);Sex=(sex=='m'?'m':'f');Age=age;}voidShowMe(){cout<<"姓名:"<<Name<<endl;cout<<"性别:"<<(Se
x=='m'?"男":"女")<<endl;cout<<"年龄:"<<Age<<endl;}};classTeacher:publicPerson{charDept[20];intSalary;publ
ic:Teacher(char*name,intage,charsex,char*dept,intsalary);voidShowMe(){Person::ShowMe();cout<<"工作单位:"<<Dept<<endl;cout<<"月薪:"<<Salary<<endl<<endl
;}};Teacher::Teacher(char*name,intage,charsex,char*dept,intsalary){Register(name,age,sex);strcpy(Dept,dept);S
alary=salary;}程序设计与问题求解Ⅱ44多派生(续)classStudent:publicPerson{charID[12];charClass[12];public:Student(char*name,intage
,charsex,char*id,char*classid);voidShowMe(){cout<<"学号:"<<ID<<endl;Person::ShowMe();cout<<"班级:"<<Class<<"\n";}};Student::Student(char*name,int
age,charsex,char*id,char*classid){Register(name,age,sex);strcpy(ID,id);strcpy(Class,classid);}intmain(){Teacherteach1("章立早",38,'m',"计
算机学院",2300);Studentstd1("李木子",22,'f',"02035003","信管01");teach1.ShowMe();std1.ShowMe();return0;}程序设计与问题求解Ⅱ45单继承//Point.h文件Poi
nt类的声明#ifndefPOINT_H#definePOINT_HclassPoint{intx,y;//点的x和y坐标public:Point(int=0,int=0);voidSetPoint(int,int);//设置坐标intGetX(){returnx;}//取x坐标intG
etY(){returny;}//取y坐标voidPrint();//输出点的坐标};#endif//Point.cpp文件Point类的成员函数定义#include<iostream>usingnamespaces
td;#include"point.h"Point::Point(inta,intb){SetPoint(a,b);}voidPoint::SetPoint(inta,intb){x=a;y=b;}voidPoint::Print(){cout<<'['<<x<<","<<y<<']
';}程序设计与问题求解Ⅱ46单继承(续)//Circle.h文件Circle类的声明#ifndefCIRCLE_H#defineCIRCLE_H#include<iostream>usingnamespacest
d;#include"point.h"classCircle:publicPoint{doubleradius;public:Circle(intx=0,inty=0,doubler=0.0);voidSetRadius
(double);//设置半径doubleGetRadius();//取半径doubleArea();//计算面积voidPrint();//输出圆心坐标和半径};#endif//Circle.cpp文件Circle类的成员函数定义#include<iostream>usingnamespac
estd;#include"circle.h"Circle::Circle(inta,intb,doubler):Point(a,b){SetRadius(r);}voidCircle::SetRadius(doubler){radius=(r>=0?r:0);}dou
bleCircle::GetRadius(){returnradius;}doubleCircle::Area(){return3.14159*radius*radius;}voidCircle::Print
(){cout<<"Center=";Point::Print();cout<<";Radius="<<radius<<endl;}程序设计与问题求解Ⅱ47单继承(续)//Example11-5.cpp文件#i
nclude<iostream>usingnamespacestd;#include"point.h"#include"circle.h"intmain(){Pointp(30,50);Circlec(120,80,10.0);cout<<"Pointp:";p.P
rint();cout<<"\nCirclec:";c.Print();cout<<"Thecentreofcirclec:";c.Point::Print();cout<<"\nTheareaofcirclec:"<<c.Are
a()<<endl;return0;}程序设计与问题求解Ⅱ48多继承时派生类的声明class派生类名:继承方式1基类名1,继承方式2基类名2,…{……};注意:每个继承方式,仅限制紧随其后的基类的继承。
多继承的声明程序设计与问题求解Ⅱ49构造函数的调用次序:调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左到右)。调用对象成员的构造函数,调用顺序按照它们在类中声明的顺序。执行派生类的构造函数体。多继承的构造函数与析构函数程序设计与问题求解Ⅱ
50多继承//base1.h文件#ifndefBASE1_H#defineBASE1_HclassBase1{intvalue;public:Base1(intx){value=x;}intgetData()const{returnvalue;}};#
endif//base2.h文件#ifndefBASE2_H#defineBASE2_HclassBase2{charletter;public:Base2(charc){letter=c;}chargetData()const{returnlette
r;}};#endif程序设计与问题求解Ⅱ51多继承(续)//derived.h文件#ifndefDERIVED_H#defineDERIVED_H#include"base1.h"#include"bas
e2.h"//多继承classDerived:publicBase1,publicBase2{doublereal;//派生类私有的数据成员public:Derived(int,char,double);doublegetReal()const
;voidOutput();};#endif//derived.cpp文件#include<iostream>usingnamespacestd;#include"derived.h"Derived::Derived(inti,c
harc,doublef):Base1(i),Base2(c),real(f){}doubleDerived::getReal()const{returnreal;}voidDerived::Output(){cout<<"Integer:"<<Base1::getData()<<"\
nCharacter:"<<Base2::getData()<<"\nRealnumber:"<<real<<endl;}程序设计与问题求解Ⅱ52//Example11-7.cpp文件多重派生#inc
lude<iostream>usingnamespacestd;#include"base1.h"#include"base2.h"#include"derived.h"intmain(){Base1b1(10),*base1Ptr=0;Base2
b2('Z'),*base2Ptr=0;Derivedd(7,'A',3.5);//对象输出各自的数据成员cout<<"Objectb1containsinteger"<<b1.getData()<<"\n
Objectb2containscharacter"<<b2.getData()<<"\nObjectdcontains:\n";d.Output();//派生类对象对基类成员函数的访问cout<<"DatamembersofDerivedcanbe"<<"accessedin
dividually:";cout<<"\nInteger:"<<d.Base1::getData()<<"\nCharacter:"<<d.Base2::getData()<<"\nRealnumber:"<<d.getReal()<<endl;cout<<"Derivedcanbetreate
dasan"<<"objectofeitherbaseclass:\n";//派生类对象作为Base1对象base1Ptr=&d;cout<<"base1Ptr->getData()yields"<<base1Ptr->getD
ata()<<'\n';//派生类对象作为Base2对象base2Ptr=&d;cout<<"base2Ptr->getData()yields"<<base2Ptr->getData()<<endl;return0;}程序设计与问题
求解Ⅱ53赋值兼容规则在派生类对象和基类对象之间赋值时需要注意赋值的方向,赋值操作应满足赋值兼容原则(向上兼容):基类指针可以指向派生类对象。可以把派生类对象赋值给基类对象。派生类对象可以初始化基类
对象的引用。通过基类对象名、指针只能使用派生类从基类继承的成员(不能使用派生类新增的成员)程序设计与问题求解Ⅱ54赋值兼容规则举例(1)classA{intx;public:voidsetX(intm){x=m;}voidp
rintX(){cout<<x<<'\n';}};classB:publicA{inty;public:voidsetY(intm){y=m;}voidprintY(){cout<<y<<'\n';}};voidmain(){Aa;
a.setX(2);a.printX();Bb;b.setX(3);b.setY(4);b.printX();b.printY();Ac=b;c.printX();}程序设计与问题求解Ⅱ55赋值兼容规则举例(2)#
include<iostream>usingnamespacestd;classPet//基类{public:voidSpeak(){cout<<"Howdoesapetspeak?"<<endl;}};classCat:publicPet//派生类{public:voidSpeak(){
cout<<"miao!miao!"<<endl;}};classDog:publicPet//派生类{public:voidSpeak(){cout<<"wang!wang!"<<endl;}};intmain(){Petobj,*p1;//基类对象指针p1,基类对象objDogdo
g1;Catcat1;obj=dog1;//用Dog类对象给Pet类对象赋值obj.Speak();p1=&cat1;//用Cat类对象地址给基类指针赋值p1->Speak();p1=&dog1;//用Dog类对象地址给基类指针赋值p1->Speak();Pet&
p4=cat1;//以Cat类对象初始化Pet类引用p4.Speak();return0;}程序设计与问题求解Ⅱ56classA{public:inta;voiddisplay(){};};classB{public:inta;voiddis
play(){};};classC:publicA,publicB{public:inta;voidshow(){};};voidmain(){Cc1;c1.a=3;//执行哪一个?c1.display();//执行哪一个?}两个基类有同
名成员程序设计与问题求解Ⅱ57A类inta;voiddisplay();B类inta;voiddisplay();C类inta;voiddisplay();intavoiddisplay();inta;voidshow();两个基类有同名成员(续)如何解决这种二义
性?程序设计与问题求解Ⅱ58二义性问题(1)在多继承时,若基类与派生类之间,或基类之间出现同名成员,将出现访问时的二义性(不确定性)。当同一个基类被同一个派生类间接继承多次的时候,将在该派生类中产生该基类的多个副本。此时访问该基
类中的成员,将产生二义性。程序设计与问题求解Ⅱ59二义性问题(2)解决二义性的方法同名覆盖(隐藏)原则如果派生类与基类之间出现同名成员,这时系统自动用派生类的成员名覆盖(隐藏)基类成员名,派生类成
员名优先。类名限定法不同基类之间出现同名成员,使用基类名来显式指定成员基类名::数据成员名基类名::成员函数名(参数表);程序设计与问题求解Ⅱ60classA{public:inta;voiddisplay(
){};};classB{public:inta;voiddisplay(){};};classC:publicA,publicB{public:inta;voidshow(){};};voidmain(){Cc1;c1.a=3;//不存在二义性c1.displ
ay();//存在二义性}同名覆盖原则程序设计与问题求解Ⅱ61classA{public:inta;voiddisplay(){};};classB{public:inta;voiddisplay(){};};classC:publicA
,publicB{public:inta;voidshow(){};};voidmain(){Cc1;c1.a=3;//不存在二义性c1.B::display();//不存在二义性}类名限定法程序设计
与问题求解Ⅱ62虚基类定义虚基类来解决二义性当同一个基类被同一个派生类间接继承多次的时候,可将该基类定义为虚基类,使从不同路径继承过来的该类成员在派生类中只拥有一个副本,从而消除二义性问题。虚基类的说明是在派生类的定义中:class派生类名:virt
ual继承方式基类名{…..}程序设计与问题求解Ⅱ63例如:classB{public:intb;};classB1:publicB{private:intb1;};classB2:publicB{private:intb2;
};classC:publicB1,publicB2{public:intf();private:intd;};classC{f(),d}classB1{b1}classB2{b2}classB{b}classB{b}c.bc.b1c.bc.b2
c.dBBB1B2C多派生类C的对象的存储结构示意建立C类对象时,将产生两个B的成员副本,一个是由B1继承而来的,另一个是由B2继承而来的。程序设计与问题求解Ⅱ64classC{d}classB1{b1}classB2{b2}classB{b}带有虚基类的派
生类C的对象的存储结构示意例如:classB{public:intb;};classB1:virtualpublicB{private:intb1;};classB2:virtualpublicB{private
:intb2;};classC:publicB1,publicB2{private:floatd;};同一个基类被同一个派生类间接继承多次的时候,将在该派生类中产生该基类的多个副本。将此基类声明为虚基类,可使其在该派生类中只产生一个副本。建立虚基类的子对象时,虚基类构造函数仅被调用
一次。c.b1c.b2c.dc.b程序设计与问题求解Ⅱ65虚基类的初始化(1)如果在虚基类中定义了带参数的构造函数,而没有定义默认构造函数,则在其所有派生类(包括直接派生或间接派生类)中,通过构造函数的初始化表对虚基类进行初始化。普通的多层派生,在
派生类的构造函数中只需调用其直接基类的构造函数。程序设计与问题求解Ⅱ66#include<iostream>usingnamespacestd;classA{public:A(inti){cout<<"constrctorA:"<<endl;}
};classB:virtualpublicA{public:B(intn):A(n){cout<<"constructorB:"<<endl;}};classC:virtualpublicA{public:C(intn):A(n){cout<<"costructorC:"<<endl
;}};classE:publicB,publicC{public:E(intn):A(n),B(n),C(n){cout<<"constructorE:"<<endl;}};voidmain(){Ee(1);}虚基类的初始化(2)程序设计与问题求解Ⅱ67综合练习classA
{public:A(){cout<<"consA\n";}A(constA&a){cout<<"copyA\n";}~A(){cout<<"desA\n";}};classB{intx;public:B(intm=0):x(m){cou
t<<"consB\n";}voidf(){cout<<x<<"";}~B(){cout<<"desB\n";}};classC:publicB{Aa;public:C(inti,A&k):B(i),a(k){cout<<"consC\n";}voidf(){B::f();cout<<"in
C\n";}~C(){cout<<"desC\n";}};voidmain(){Ava;Cvc(2,va);vc.f();cout<<"main\n";}程序设计与问题求解Ⅱ68本章小结类的继承与派生派生类派生类的继承方式与访问属性派生类的构造函数和析构函
数多继承赋值兼容原则程序设计与问题求解Ⅱ69作业5复习派生类的定义复习派生类的继承方式与访问属性复习派生类的构造函数与析构函数复习多层派生复习多继承p264,习题1,2,3,4