数据结构福大2012级课件继承_第1页
数据结构福大2012级课件继承_第2页
数据结构福大2012级课件继承_第3页
数据结构福大2012级课件继承_第4页
数据结构福大2012级课件继承_第5页
已阅读5页,还剩71页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

计2023/12/111引

言2023/12/112概

顾基于过程的程序设计基于对象的程序设计面向对象的程序设计程序的主体:函数类的生成、设计类对象的使用C++过程化语言基础程序的主体:类2023/12/113结

论仅仅支持基于类的设计、生成和类对象的使用是不能称为面向对象的程序设计面向对象程序设计的四大主要特点抽象封装继承多态基于对象的程序设计面向对象的程序设计2023/12/114

要010203继承与派生(重点)多态性与虚函数(重点)函数模板(常用)04类模板(常用)2023/12/115继

生2023/12/116问

入福州大学有三类学生:本科生、硕士生、博士生是否设计成一个类student?本科生、硕士生、博士生的数据成员不尽相同是否设计三个独立的类:bachelor、master、doctor?合理吗?2023/12/1172023/12/118classbachelor{private:intID;Stringname;intage;Datedayofbirth;floatscore[40];public:bachelor(…);~bachelor();voidprint();voidget_name();……};classmaster{private:intID;Stringname;intage;Datedayofbirth;Stringadvisor;floatscore[15];public:master(…);~master(){};voidprint();voidget_name();……};classdoctor{private:intID;String*name;intage;Datedayofbirth;Stringadvisors[5];floatscore[10];public:doctor(…);~doctor(){};voidprint();voidget_name();……};问

析共同的数据属性在3个类中各自描述一次,显得繁琐,重复,能够简化为统一描述?分析姓名、学号、年龄等数据属性是所有学生都有的共性,本科生、硕士生和博士生是学生的细化和特化(P.118)先统一描述所有学生的共性数据以及对共性数据的处理功能;描述某一类学生时,首先说明它是学生的一种,然后再逐一描述这种学生的个性——面向对象程序设计中类的继承与派生2023/12/1192023/12/1110classbachelor

:publicstudent{private:floatscore[40];public:bachelor(…);~bachelor();voidprint();……};classmaster

:publicstudent{private:Stringadvisor;floatscore[15];public:master(…);~master(){};voidprint();……};classdoctor

:publicstudent{private:Stringadvisors[3];floatscore[10];public:doctor(…);~doctor(){};voidprint();……};classstudent{private:public:intID;student(…);String*name;~student();intage;voidget_name();Datedayofbirth;voidget_age();……};继

生类

承派生类可以获得基类的已有特性派生类继承了基类的所有数据成员和成员函数类的继承是层次结构的基类是派生类的抽象类的派生基类产生了一个和具有基类各种特性的新的子类派生类可以对成员作必要的增加和调整派生类又可以作为基类再派生出新的派生类派生类是基类的具体化继承派生在一个已经存在的类(基类/父类)的基础之上建立一个新的类(派生类/子类)2023/12/1111单

承单一继承(P.119)一个派生类只能由一个基类派生而来一个子类只有一个父类图b的情况中对于类Y1和类Y2而言仍然是单一继承方式的派生类多重继承(P.119)一个派生类由多个(>=2)基类派生而成一个子类有两个或者两个以上的父类注意区分图C和图d的多级继承和多重继承2023/12/1112继

式classA{inti;//基类成员};classB:publicA//A派生了B//B继承了A,B自动拥有A的成员{intj;//定义派生类的新成员};2023/12/1113派

明一般形式(P.118)class<派生类标识符>:<派生方式><基类标识符>{<派生类新定义成员>};2023/12/1114派

例基类(普通学生类)classstudent{private:intID;char*name;floatscore[11];public:student(intn1,char*nm1){……}~student(){deletename;}voidprint(){…..}voidget_name(){……}……};派生类声明(大学生类)classundergstudent

:publicstudent{private:char*major;//新增加的数据成员floatscore[40];public:……voidget_data{……}print_major(){……}//新增加的成员函数voidprint(){……}}2023/12/1115基类(普通学生类)派生类声明(大学生类)大学生类(未考虑访问属性)

intID;char*name;

floatscore[40];

char*major;

print_major(){……}

voidprint(){…..}…….2023/12/1116派

成两大部分从基类继承来的成员声明派生类时增加的部分每一部分都分别包括数据成员和成员函数构成方式并非基类的成员和派生类增加成员的简单加和接收基类的全部成员(不包括构造函数和析构函数)调整从基类接收的成员(指定派生方式改变访问属性)声明派生类时增加的成员根据需要增加成员定义派生类的构造函数和析构函数(不能从基类继承而来)2023/12/1117基

整无条件地全部接收不接收析构函数和构造函数接收到的基类成员可以调整通过指定派生方式改变成员的访问属性可以声明同名成员加以覆盖2023/12/1118派

式三种派生方式publicprivateprotected不同的派生方式决定了基类成员在派生类中访问属性2023/12/1119派

访

性分情况处理基类的成员函数访问基类成员

✔派生类成员函数访问派生类自己增加的成员

✔基类的成员函数访问派生类的成员

✗派生类外访问派生类的成员根据成员的访问属性判定能否由类外访问该成员派生类的成员函数访问基类的成员派生类外访问基类的成员核心问题:如何确定基类成员在派生类中的访问属性基类成员声明的访问属性派生类对基类的继承方式2023/12/1120三

访

继承

方式基类成员公有继承私有继承保护继承公有成员公有私有保护私有成员派生类成员不可访问派生类成员不可访问派生类成员不可访问保护成员保护私有保护2023/12/1121公

承基类的私有成员并没有成为派生类的私有成员基类的私有成员仅仅只有基类的成员函数才能应用基类的私有成员是派生类的不可访问的成员基类的私有成员只能通过基类的公有成员函数加以访问实例:基类student派生类undergstudent(公有继承)假设一创建一个对象freshman是一个大一新生(Bob,10009,CS,……)如何能够正确获取name、ID、major等信息?2023/12/1122方

一主函数调用freshman的公有成员函数get_data派生类中get_data如下定义:cout<<name;cout<<ID;cout<<major;原因:name和ID是基类的私有成员,在派生类中不可访问2023/12/1123方

二基类中公有成员函数get_namecout<<name;基类中公有成员函数get_IDcout<<ID;派生类中公有成员函数get_majorcout<<major;主函数中分别调用freshman的get_name、get_ID和get_major完成信息的获取公有继承下,基类的公有成员在派生类中仍为公有,可在派生类外访问2023/12/1124方

三主函数调用freshman的公有成员函数get_data派生类中get_data如下定义:get_name();//调用基类的成员函数get_ID();//调用基类的成员函数cout<<major;基类中公有成员函数get_name定义如下:cout<<name;基类中公有成员函数get_ID定义如下:cout<<ID;2023/12/1125公

伸特点:较好地保留了基类的特征几乎具有基类的全部功能公有继承方式下的公有派生类是基类的子类型基类和公有派生类之间的类型转换回顾:不同数据类型之间在一定条件下可以进行类型的转换——赋值兼容example:整型数据可以复制给双精度型的变量基类对象和子类(公有派生类)之间也有赋值兼容关系2023/12/1126基

换派生类对象向基类对象赋值赋值时舍弃派生类自己的成员赋值后基类对象的成员并未扩充//set_major是undergstudent的公有成员函数undergstudentugS1(Bob,10009,CS,….);studentS1;S1=ugS1;//✔S1.set_major(“EE”);//✗ugS1.set_major(“EE”);//✔不能用基类对象对子类对象赋值同一基类的不同子类对象之间不能赋值2023/12/1127派生类对象可以向基类对象的引用进行赋值或者初始化回顾:变量x的引用是变量x的别名,和变量x共享同一存储空间studentS1;undergstudentugS1(Bob,10009,CS,….);student&S1_ref=S1;S1_ref=ugS1;//✔,用子类对象ugS1对S1_ref进行赋值student&S2_ref=ugS1;/*✔,定义了一个student对象的引用S2_ref,并用ugS1进行初始化*/S1_ref.set_major(“EE”);//✗S1_ref和S2_ref仍然只是基类对象的引用,并不是ugS1(子类对象)的别名,S1_ref、S2_ref和ugS1只共享了ugS1中属于基类部分的存储空间2023/12/1128如果函数的参数是基类对象或者基类对象的引用,相应的实参可以用子类对象参数传递时完成自动类型转换派生类对象的地址可以赋给指向基类对象的指针变量指向基类对象的指针变量也可以指向派生类对象指向基类对象的指针变量只能访问派生类中的基类成员,而不能访问派生类增加的成员studentS1;undergstudentugS1(Bob,10009,CS,….);student*S_ptr=&S1;S_ptr=&ugS1;/*✔,子类对象ugS1的地址可以赋值给指向基类对象的指针变量*/S_ptr->set_major(“EE”);//*✗不能访问派生类增加的成员2023/12/1129私

承私有基类的公有成员和保护成员相当于派生类中的私有成员派生类的成员可以访问私有基类的公有成员和保护成员,但是派生类外不能访问实例:情况与前述相同,唯一的区别是派生类undergstudent以私有继承的方式由student类派生而成2023/12/1130方

一主函数调用freshman的公有成员函数get_data派生类中get_data如下定义:cout<<name;cout<<ID;cout<<major;原因:name和ID是基类的私有成员,公有继承下在派生类中已不可访问,私有继承下依然不可行2023/12/1131方

二基类中公有成员函数get_namecout<<name;基类中公有成员函数get_IDcout<<ID;派生类中公有成员函数get_majorcout<<major;主函数中分别调用freshman的get_name、get_ID和get_major完成信息的获取原因:私有继承下,基类的公有成员等同于派生类的私有成员,无法在派生类外访问2023/12/1132方

三主函数调用freshman的公有成员函数get_data派生类中get_data如下定义:get_name;//调用基类的成员函数get_ID;//调用基类的成员函数cout<<major;基类中公有成员函数get_name定义如下:cout<<name;基类中公有成员函数get_ID定义如下:cout<<ID;2023/12/1133保

承保护成员受保护的成员不能为类外访问,但是可以被派生类的成员函数所引用基类(普通学生类)classstudent{private:intID;char*name;floatscore[11];public:……};派生类声明(大学生类)classundergstudent

:publicstudent{private:char*major;//新增加的数据成员floatscore[40];public:……}classstudent{protected:intID;char*name;floatscore[11];public:……};2023/12/11342023/12/1135保

承保护基类的公有成员和保护成员相当于派生类中的保护成员派生类的成员可以访问保护基类的公有成员和保护成员,但是派生类外不能访问实例:情况与前述相同,但是派生类undergstudent以保护继承的方式由student类派生而成2023/12/1136第

形(ID和name为私有数据)基类(普通学生类)classstudent{private:intID;char*name;floatscore[11];public:student(intn1,char*nm1){……}~student(){deletename;}voidprint(){…..}voidget_name(){……}……};派生类声明(大学生类)classundergstudent

:protectedstudent{private:char*major;//新增加的数据成员floatscore[40];public:……voidget_data{……}print_major(){……}//新增加的成员函数voidprint(){……}}2023/12/1137方

一主函数调用freshman的公有成员函数get_data派生类中get_data如下定义:cout<<name;cout<<ID;cout<<major;原因:name和ID是基类的私有成员,保护继承下在派生类中依然不可访问2023/12/1138方

二基类中公有成员函数get_namecout<<name;基类中公有成员函数get_IDcout<<ID;派生类中公有成员函数get_majorcout<<major;主函数中分别调用freshman的get_name、get_ID和get_major完成信息的获取原因:保护继承下,基类的公有成员等同于派生类的保护成员,无法在派生类外访问2023/12/1139方

三主函数调用freshman的公有成员函数get_data派生类中get_data如下定义:get_name;//调用基类的成员函数get_ID;//调用基类的成员函数cout<<major;基类中公有成员函数get_name定义如下:cout<<name;基类中公有成员函数get_ID定义如下:cout<<ID;2023/12/1140第二种情形

(ID和name为保护成员数据)基类(普通学生类)classstudent{protected:

intID;char*name;private:floatscore[11];public:student(intn1,char*nm1){……}~student(){deletename;}voidprint(){…..}voidget_name(){……}……};派生类声明(大学生类)classundergstudent

:protectedstudent{private:char*major;//新增加的数据成员floatscore[40];public:……voidget_data{……}print_major(){……}//新增加的成员函数voidprint(){……}}2023/12/1141方

一主函数调用freshman的公有成员函数get_data派生类中get_data如下定义:cout<<name;cout<<ID;cout<<major;原因:name和ID是基类的保护成员,公有继承下和保护继承下派生类中可以访问2023/12/1142方

二基类中公有成员函数get_namecout<<name;基类中公有成员函数get_IDcout<<ID;派生类中公有成员函数get_majorcout<<major;主函数中分别调用freshman的get_name、get_ID和get_major完成信息的获取原因:保护继承下,基类的公有成员等同于派生类的保护成员,无法在派生类外访问2023/12/1143方

三主函数调用freshman的公有成员函数get_data派生类中get_data如下定义:get_name;//调用基类的成员函数get_ID;//调用基类的成员函数cout<<major;基类中公有成员函数get_name定义如下:cout<<name;基类中公有成员函数get_ID定义如下:cout<<ID;2023/12/1144保

用在类的层次继承结构中找到数据共享与成员隐蔽之间的最佳均衡如果需要在派生类中引用基类的某些成员,应当将基类中的这些成员声明为protected,而非private2023/12/1145以student类中的ID和name为例如果声明成为private,将造成派生类无法访问的局面,使得派生类的使用很不方便如果声明成为public,使得基类的数据隐蔽性受到一定的损伤如果声明成为protected,派生类可以访问从而使得基类和派生类之间可以实现数据共享,基类和派生类的外部都不能访问,保障了数据的隐蔽性2023/12/1146多

访

性类的层次继承结构导致了类的多级派生基类基类的派生类派生类的派生类……直接派生类vs.间接派生类直接基类vs.间接基类多级派生情况下各成员的访问属性在基类和其直接派生类之间按照一级派生访问属性的原则确定各成员的访问属性后逐级向下迁移2023/12/1147组合类的构造函数(子对象)组合类:类的数据成员本身就是一个对象实例classPerson{Stringname;//name是由类String创建的对象,可称为子对象intage;public:Person(…);~Person(){};…}2023/12/1148Person的构造函数该如何处理其子对象数据成员name的初始化?参

表在构造函数头之后,函数体之前,由冒号开头,逗号分隔,每一部分形式为:数据成员名(初始值)Person(constString&nm=“”,inta=18)

:name(nm),age(a)//参数初始表

{}//即使函数体为空,该处大括号也不能省略适用情形子对象所属类没有缺省构造函数,必须采用参数初始化表子对象的构造函数复杂,用参数初始化表可提高效率常量型数据成员或引用型数据成员,必须在参数初始化表中赋初值2023/12/1149派

数回顾:构造函数的作用对类中的数据成员进行初始化派生类的构造函数的特殊性派生类的数据成员构成较为特殊从基类中接收来的数据成员派生类自己增加的数据成员派生类并没有继承基类的构造函数派生类的构造函数的初始化工作基类数据成员的初始化工作

派生类增加的数据成员的初始化工作2023/12/1150派

例基类(普通学生类)classstudent{private:intID;char*name;floatscore[11];public:student(intn1,char*nm1){……}~student(){deletename;}voidprint(){…..}voidget_name(){……}……};派生类声明(大学生类)classundergstudent

:publicstudent{private:char*major;//新增加的数据成员floatscore[40];public:……voidget_data{……}print_major(){……}//新增加的成员函数voidprint(){……}}2023/12/1151实

例:大学生类undergstudent的构造函数方案一undergstudent(intn,stringstu_name,stringstu_major,float*scoreList){ID=n;name=stu_name;major=stu_major;score=scoreList;}派生类无法访问基类的私有成员数据方案二undergstudent(intn,stringstu_name,stringstu_major,float*scoreList){student(n,stu_name);//调用基类构造函数major=stu_major;score=scoreList;}不能在派生类构造函数体中显式调用构造函数!虽然能通过编译,但结果不对2023/12/1152派

道方案三undergstudent(intn,stringstu_name,stringstu_major,float*scoreList):student(n,stu_name);//调用基类构造函数{/*派生类的函数体中只对派生类新增的数据成员进行初始化*/major=stu_major;score=scoreList;}解决的办法是通过成员初始化表来完成基类数据成员的初始化,在参数初始化表中可以显式调用基类构造函数派生类的函数体完成新增数据成员的初始化2023/12/1153派

式<派生类名>(<总参数表>):<基类构造函数名>(<参数表1>){<派生类数据成员的初始化>};说明:1、如果派生类构造函数在类的外面定义,类体中只需要写这个函数的声明:<派生类名>(<总参数表>);2、总参数表中的参数包括了基类构造函数所需的参数和派生类新增数据成员初始化所需的参数

生成一个类对象(10009,bob,CS,……)

undergstudent(n,stu_name,stu_major,scorelist):student(n,stu_name)构造函数的参数初始化表2023/12/1154有

数子对象类的数据成员本身就是一个类对象classundergstudent

:publicstudent{private:char*major;floatscore[40];

Teacher*tutor;//子对象public:……}classteacher

{private:intID;char*name;

char*title;public:……}2023/12/1155分析:对象建立时需要对它的数据成员进行初始化派生类构造函数对其数据成员进行初始化的时候也需要对其中的子对象进行初始化在成员初始化表中显式调用子对象的构造函数undergstudent(intn,stringstu_name,stringstu_major,float*scoreList,intT_n,stringT_name,stringT_title):student(n,stu_name),tutor(T_n,T_name,T_title)2023/12/1156派

式<派生类名>(<总参数表>):<基类名>(<参数表1>),<对象成员名>(<参数表2>){<派生类其它数据成员的初始化>};派生类构造函数的任务初始化基类数据成员初始化子对象数据成员初始化其它派生类数据成员派生类构造函数执行顺序调用基类构造函数调用子对象构造函数执行派生类构造函数体2023/12/1157特

数多层派生时,不需要在成员初始化表中列出其上每一层派生类的构造函数,只需要列出其直接基类的构造函数即可派生类新增成员无需任何初始化时,派生类构造函数的函数体为空如果基类没有定义构造函数,或者定义了没有参数的构造函数,派生类定义构造函数可以不写基类构造函数,派生类构造函数调用时,系统会自动首先调用基类的默认构造函数2023/12/1158派

数回顾:析构函数的作用在对象撤销之前,进行必要的清理工作派生类的析构函数对派生类新增加的成员进行清理根据需要定义相应的析构函数如果有子对象,还需要对子对象进行清理调用子对象的析构函数完成需要对接收自基类的成员进行清理调用基类的析构函数完成自动调用基类和子对象的析构函数2023/12/1159派生类析构函数的任务清理基类数据成员清理子对象数据成员清理其它派生类数据成员派生类析构函数执行顺序调用基类析构函数调用子对象析构函数执行派生类析构函数部分2023/12/1160派

盖覆盖规则基类的同名成员在派生类中被屏蔽,成为不可见的定义在派生类对象模块中通过对象名访问同名的成员,访问的是派生类的成员实例:studentstudent_A;undergstudentstudent_B;student_A.print();//调用基类中的成员函数printstudent_B.print();//调用派生类中的成员函数printstudent_B.student::print();//指明作用域而调用基类成员数据成员的覆盖只要命名相同即可成员函数的覆盖不仅函数名要相同,函数的参数表包括参数个数和类型都要相同2023/12/1161多

承一个类可以从一个或者多个基类派生而来。根据派生类继承基类的个数,将继承分为单继承和多继承。当派生类有多个基类时称为多继承。单继承可以看作是多继承的一个特例,多继承可以看作是多个单继承的组合,它们有很多相同特性。实例教师类在职研究生类研究生类2023/12/1162多

义class<派生类名>:<继承方式><基类名1>,…,<继承方式><基类名n>{<派生类新定义成员>};2023/12/1163多

数成员初始化表中需要包含多个基类构造函数一般形式<派生类名>(<总参数表>):<基类名1>(<参数表1>),…,<基类名n>(<参数表n>){<派生类数据成员的初始化>};2023/12/1164多

题情形一:两个基类有同名成员2023/12/1165classgstudent:

{protected:intID;char*name;char*major;public:init();……}classteacher

{protected:intID;char*name;

char*title;public:init();……}classonjobgstudent:publicgstudent,publicteacher

{public:voidprint();{cout<<ID<<endl;cout<<name<<endl;}……}main()

{onjobgstudentTeach_A;

Teach_A.init();//该调用哪个init()?

Teach_A.print();//print函数该输出哪个ID…?//研究生学号和教师工号并不同}二义性2023/12/1166解

一用作用域运算符“::”进行限定,显式访问基类成员。main()

{onjobgstudentTeach_A;

Teach_A.Teacher::init();Teach_A.gstudent::init();

Teach_A.print();}classonjobgstudent:publicgstudent,publicteacher

{public:voidprint();{cout<<Teacher::ID<<endl;cout<<Teacher::name<<endl;cout<<gstudent::ID<<endl;cout<<major<<endl;}……}派生类的成员函数访问基类成员,不必写对象名2023/12/1167情

二:两个基类和派生类都有同名成员classgstudent:

{protected:intID;char*name;char*major;public:voidinit(…);……}classteacher

{protected:intID;char*name;

char*title;public:voidinit(…);……}classonjobgstudent:publicgstudent,publicteacher

{public:voidinit(…);{

}voidprint();……}main()

{onjobgstudentTeach_A;

Teach_A.init(…);//调用哪个init?

//根据同名覆盖原则//调用onjobgstudent的init

Teach_A.print();}无二义性2023/12/1168内在问题分析Teacher_A.init调用类onjobgstudent的init函数进行初始化,而类onjobgstudent的init函数分别调用了gstudent的init函数和teacher

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论