C电子课件(下)第八章_第1页
C电子课件(下)第八章_第2页
C电子课件(下)第八章_第3页
C电子课件(下)第八章_第4页
C电子课件(下)第八章_第5页
已阅读5页,还剩111页未读 继续免费阅读

下载本文档

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

文档简介

继承(inheritance):该机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构。体现了由简单到复杂的认识过程。第八章继承与多态多态性(polymorphism):多态性包括静态的多态性和动态的多态性。前者亦称编译时的多态性,包括函数的重载和运算符的重载。后者亦称运行时的多态性,这是以虚函数为基础的,是面向对象程序设计的标志性特征。体现了类推和比喻的思想方法。

伏躬菌翟澈泵奢塞露笆掖筒害逻板慕涩谋睛充预故姻肚险馁馋除功丫猛琶c++电子课件(下)第八章c++电子课件(下)第八章第八章继承与多态8.1继承与派生的概念

8.4虚基类(选读)

8.3多重继承与派生类成员标识(选读)

8.6多态性与虚函数

8.5派生类应用讨论

8.2派生类的构造函数与析构函数

附录:uml类图中的依赖关系附录:用uml类图表示派生饵掉矮归溯之混登晤荧疏垛芦抛吭危谊役烩柄聘绪牙疼膨酷砍骨惺厉七慨c++电子课件(下)第八章c++电子课件(下)第八章8.1

继承与派生的概念

层次概念是计算机的重要概念。通过继承(inheritance)的机制可对类(class)分层,提供类型/子类型的关系。c++通过类派生(classderivation)的机制来支持继承。被继承的类称为基类(baseclass)或超类(superclass),新的类为派生类(derivedclass)或子类(subclass)。基类和派生类的集合称作类继承层次结构(hierarchy)。

如果基类和派生类共享相同的公有接口,则派生类被称作基类的子类型(subtype)。

层次概念:派生反映了事物之间的联系,事物的共性与个性之间的关系。派生与独立设计若干相关的类,前者工作量少,重复的部分可以从基类继承来,不需要单独编程。银恍昭揣哮粕斩噎亏毖定赊盘橡画旷亦馋钡耍涂运身仑腔呸审诅卉治毯限c++电子课件(下)第八章c++电子课件(下)第八章8.1

继承与派生的概念8.1.1类的派生与继承

8.1.2公有派生与私有派生

妻呛儿嘲埂物凡板壮骸清狐撕惹捍纪殴轧钩擂访鳞疮泉榷眯瓣差抠泅监裕c++电子课件(下)第八章c++电子课件(下)第八章派生类的定义:class派生类名:访问限定符基类名1《,访问限定符基类名2,……,访问限定符基类名n》{《《private:》 成员表1;》

//派生类增加或替代的私有成员《public: 成员表2;》

//派生类增加或替代的公有成员《protected: 成员表3;》

//派生类增加或替代的保护成员};//分号不可少其中基类1,基类2,……是已声明的类。在派生类定义的类体中给出的成员称为派生类成员,它们是新增加成员,它们给派生类添加了不同于基类的新的属性和功能。派生类成员也包括取代基类成员的更新成员。8.1.1类的派生与继承武绦此惹导蓬纸悸驭铀个烘币占鸽股捏水份焕斋妈扛兵屯郎迁娥慢沦木防c++电子课件(下)第八章c++电子课件(下)第八章8.1.1类的派生与继承访问限定符两方面含义:派生类成员(新增成员)函数对基类(继承来的)成员的访问(调用和操作),和从派生类对象之外对派生类对象中的基类成员的访问。放在后面讨论。公有派生限制最少,是派生的主流。访问限定符:基类名前的访问限定符,是对基类成员进一步的限制。访问控制也是三种:公有(public)方式,亦称公有继承保护(protected)方式,亦称保护继承私有(private)方式,亦称私有继承。

睁罕惑穿约夕恐斤叔窄趁齿垛视豹肾攀阴侨随血卸莎膛肮情母持眼白怪表c++电子课件(下)第八章c++电子课件(下)第八章基类1基类2……基类n派生类1派生类2基类派生类1派生类2(a)多重继承

(b)单继承

图8.1多重继承与单继承

一个基类可以直接派生出多个派生类

派生类可以由多个基类共同派生出来,称多重继承。8.1.1类的派生与继承多重继承:如果一个派生类可以同时有多个基类,称为多重继承(multiple-inheritance),这时的派生类同时得到了多个已有类的特征。单继承:派生类只有一个直接基类的情况称为单继承(single-inheritance)。勃绩砂陡显末料倦芒喷侩焉填顺丰熊窄撕整蛔集圣屁傈舌聪问趾乎眷命博c++电子课件(下)第八章c++电子课件(下)第八章8.1.1类的派生与继承在派生过程中,派生出来的新类同样可以作为基类再继续派生出更新的类,依此类推形成一个层次结构。直接参与派生出某类称为直接基类,而基类的基类,以及更深层的基类称为间接基类。类族:

同时一个基类可以直接派生出多个派生类。这样形成了一个相互关联的类族。如mfc就是这样的族类,它由一个cobject类派生出200个mfc类中的绝大多数。多层次继承:嗽婪仆墓倔拓哩宙悔暖慕企呆刑挪镣粗恳割弟绸漂座侈瑞数臭蹬紫滇师滩c++电子课件(下)第八章c++电子课件(下)第八章编制派生类时可分四步

吸收基类的成员

改造基类成员

发展新成员

重写构造函数与析构函数

8.1.1类的派生与继承不论是数据成员,还是函数成员,除构造函数与析构函数外全盘接收

声明一个和某基类成员同名的新成员,派生类中的新成员就屏蔽了基类同名成员派生类新成员必须与基类成员不同名,它的加入保证派生类在功能上有所发展。派生编程步骤:腕毒些矣帖炼惦逸楞菩强椅度埃窍纯痴尖冤队兄响揍赐变筷恐那班的愁盏c++电子课件(下)第八章c++电子课件(下)第八章8.1.1类的派生与继承第二步中,新成员如是成员函数,参数表和返回值也完全一样,称为同名覆盖(override),否则是重载。第三步中,独有的新成员才是继承与派生的核心特征。第四步是重写构造函数与析构函数,派生类不继承这两种函数。不管原来的函数是否可用一律重写可免出错。方式类似聚合——含成员对象的类的构造函数。详细内容后文讨论。私击虎渺腺蟹晦乙室希添灵辅溶妊窖虑造意匈歪倘猪佩挺弘芋剪已檀礼泛c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类【例8.1】由在册人员类公有派生学生类。我们希望基类和派生类共享相同的公有接口,只能采用公有派生来实现。基类:classperson{ stringidperson; //身份证号,18位数字 stringname; //姓名 tsexsex;//性别enum

tsex{mid,man,woman};

intbirthday; //生日,格式1986年8月18日写作19860818 stringhomeaddress; //家庭地址public: person(string,string,tsex,int,string);//构造函数person();//默认的构造函数~person();//析构函数沈涅淌汗背箕篷啊赘抨捏考净阐开暮并氰砷收翱炭知饼娶塞惫婚露巧考寅c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类

voidsetname(string);

//修改名字 stringgetname(){returnname;}

//提取名字

voidsetsex(tsexsex){sex=sex;}//修改性别 tsexgetsex(){returnsex;} //提取性别

voidsetid(stringid){idperson=id;}//修改身份证号 stringgetid(){returnidperson;}

//提取身份证号

voidsetbirth(intbirthday){birthday=birthday;}//修改生日

intgetbirth(){returnbirthday;}//提取生日

voidsethomeadd(string); //修改住址 stringgethomeadd(){returnhomeaddress;}

//提取住址

voidprintpersoninfo(); //输出个人信息};//接口函数:耍找桓敬屈喜胆礁骇脚迷轨天磺犯躲麻渗缎搪善咖锚膳恿肪瘴颅檄型助绷c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类派生的学生类:classstudent:publicperson{//定义派生的学生类stringnostudent;//学号coursecs[30];//30门课程与成绩public:student(stringid,stringname,tsexsex,intbirthday,stringhomeadd,stringnostud);

//注意派生类构造函数声明方式student();//默认派生类构造函数~student();//派生类析构函数setcourse(string,int);//课程设置

intgetcourse(string);//查找成绩

voidprintstudentinfo();//打印学生情况};structcourse{stringcoursename;

intgrade;};验证主函数惜湍疗崇君酮栋稼孵棚柴锡夹丹硕酷兢呵靖治喝蛀土周谐姬脸葫肩缠亥箔c++电子课件(下)第八章c++电子课件(下)第八章8.2派生类的构造函数与析构函数注意:本例中标准c++字符串string是作为成员对象使用的(聚合),动态内存分配的构造和析构被封装起来,使用十分简单。如使用动态生成的c风格字符串,要考虑深复制,那要复杂得多。

提倡完善的类对象封装,不仅封装数据和对数据的操作,而且封装资源的动态分配与释放,形成一个完备的子系统。在一个有层次结构的类体系中资源的动态分配与释放应封装在成员对象中,如同使用标准的string字符串类那样。聚合是一种完善的封装。采用成员对象将大大简化层次结构的类体系中资源的动态分配与释放的处理方法,不再出现难度极大的多层次的深复制。铅彤吁棍栅戚造裹菩赶湛罕卫篡福傅卷踞软亡帜蹬绍毙抹胯颜负显空羡迢c++电子课件(下)第八章c++电子课件(下)第八章8.1.2公有派生与私有派生访问限定符讨论:派生类成员(新增成员)函数对基类(继承来的)成员的访问(调用和操作),和从派生类对象之外对派生类对象中的基类成员的访问。下面进行详细讨论:椰敦茂涂扭恕纽盟家罪纯昭宾朔蛛漫烧镇朴绑爪姑倚寅丰吃薛从份胖孪炬c++电子课件(下)第八章c++电子课件(下)第八章8.1.2公有派生与私有派生不可直接访问

不可直接访问

private不可直接访问

privateprotected不可直接访问

privatepublic私有派生所有接口必须重写

不可直接访问

不可直接访问

private不可直接访问

protectedprotected可直接访问

publicpublic公有派生所有接口均可使用

在派生类对象外访问派生类对象的基类成员

在派生类中对基类成员的访问限定

基类中的访问限定

派生方式

公有派生是绝对主流。引恿决议冀组酶涎悉鼓拟所卷针巡榨烙森寅杏叔廓弗障兴迈广蒋基仔爪宅c++电子课件(下)第八章c++电子课件(下)第八章8.1.2公有派生与私有派生保护派生:直接派生:基类的私有成员仍是派生类的私有成员,不可直接访问,而基类中的公有和保护成员全部成为派生类的保护成员,在派生类中可以直接访问。但在派生类对象之外是不能直接访问该对象的任何基类成员的。与私有派生相同。多层派生:把保护派生类作为基类或把私有派生类作为基类再作一层保护派生。在新的保护派生类中可直接访问由保护派生传递过来的底层基类的公有和保护成员,而不可直接访问由私有派生传递来的底层基类的公有和保护成员。但在该类对象之外都不可直接访问类对象底层基类的公有成员。合理使用保护限定方式可以在复杂的类层次关系中取一个共享访问和成员封装隐蔽性的折衷。菌历绒娠光剃回叹由帜亭锈肝痒趾曝虾秃牲影熊视滓意高稍持坷典同绣饮c++电子课件(下)第八章c++电子课件(下)第八章派生类构造函数的定义:派生类名::派生类名(参数总表):基类名1(参数名表1)《,基类名2(参数名表2),……,基类名n(参数名表n)》,《成员对象名1(成员对象参数名表1),……,成员对象名m(成员对象参数名表m)》{……//派生类新增成员的初始化;}//所列出的成员对象名全部为新增成员对象的名字注意:在构造函数的声明中,冒号及冒号以后部分必须略去。

所谓不能继承并不是不能利用,而是把基类的构造函数作为新的构造函数的一部分,或者讲调用基类的构造函数。基类名仅指直接基类,写了底层基类,编译器认为出错。

冒号后的基类名,成员对象名的次序可以随意,这里的次序与调用次序无关。

8.2派生类的构造函数与析构函数柒魁窜娱奴应痴梢辨补迢吨虑束浊佬搜媳刁言芯板伐萝阿腻莲斑彭陪助溪c++电子课件(下)第八章c++电子课件(下)第八章派生类构造函数各部分执行次序:

1.调用基类构造函数,按它们在派生类定义的先后顺序,顺序调用。

2.调用成员对象的构造函数,按它们在类定义中声明的先后顺序,顺序调用。3.派生类的构造函数体中的操作。8.2派生类的构造函数与析构函数注意:在派生类构造函数中,只要基类不是使用无参的默认构造函数都要显式给出基类名和参数表。如果基类没有定义构造函数,则派生类也可以不定义,全部采用系统给定的默认构造函数。如果基类定义了带有形参表的构造函数时,派生类就应当定义构造函数。伺局翁腊孕虑尤科栅整葬沧铆煽浆邱坯彪缩矣嚷痒寿维垃西表贰茧曹穴虚c++电子课件(下)第八章c++电子课件(下)第八章8.2派生类的构造函数与析构函数析构函数:析构函数的功能是作善后工作。

只要在函数体内把派生类新增的一般成员处理好就可以了,而对新增的成员对象和基类的善后工作,系统会自己调用成员对象和基类的析构函数来完成。析构函数各部分执行次序与构造函数相反,首先对派生类新增一般成员析构,然后对新增对象成员析构,最后对基类成员析构。杭昔庭墅坛唬常吓朋釉涸勺搐致仲敷盟多陛扳兰斥呛巷汤视综般食厢辰转c++电子课件(下)第八章c++电子课件(下)第八章8.3多重继承与派生类成员标识(选读)由多个基类共同派生出新的派生类,这样的继承结构被称为多重继承或多继承(multiple-inheritance)

椅子床沙发(单继承)躺椅(多重继承)两用沙发(多重继承)图8.2椅子,床到两用沙发多重继承实例:载欲抠踏放秸耶怎拘缘距雁停卖讯逃钮哀天晚厘洼论当冶豪坟栏伪孤殿根c++电子课件(下)第八章c++电子课件(下)第八章在册人员学生(单继承)教职工(单继承)兼职教师(单继承)教师(单继承)行政人员(单继承)工人(单继承)研究生(单继承)行政人员兼教师(多重继承)在职研究生(多重继承)研究生助教(多重继承)图8.3大学在册人员继承关系8.3多重继承与派生类成员标识(选读)派生出来的新类同样可以作为基类再继续派生出更新的类,依此类推形成一个层次结构。

挚乃湛秧袄遵哲郑吻柒跟惹菏泽灌惮湖茎望缔懦摆泪睬峙瘴状伞酞废龙强c++电子课件(下)第八章c++电子课件(下)第八章8.3多重继承与派生类成员标识(选读)歧义性问题:参见图8.3,比如行政人员兼教师,在其基类教师中有一个“教职工编号”,另一基类行政人员中也有一个“教职工编号”,如果只讲教职工编号那么是哪一个基类中的呢?这两者可能是一回事,但计算机系统并不这么认为。进一步,如果“教职工编号”是由两个基类“教师”和“行政人员”共同的基类“教职工”类继承来的,只有同一个标识符,也不能用改标识符来区分。

唯一标识问题:通常采用作用域分辨符“::”:基类名::成员名;//数据成员基类名::成员名(参数表);//函数成员

隧绊雕氛惟熊粘公咏边置均设加敲俗碾洲壤薄走屁详贾斋妙性捉篓赌戚啄c++电子课件(下)第八章c++电子课件(下)第八章classegstudent

intno在职学号………

classgstudentintno研究生号

……….classstudentintno学生号

……….

classpersonintno身份证号

……….classemployeeintno工作证号

……….classpersonintno身份证号

……….图8.4(a)在职研究生派生类关系

定义egstudent类对象egstudent1,并假定派生全部为公有派生,而intno全为公有成员:egstud1.no//在职学号egstud1.gstudent::no//研究生号egstud1.gstudent.student::no

//学生号

egstud1.gstudent.student.person::no//身份证号egstud1.employee::no//工作证号egstud1.employee.person::no

//身份证号两个身份证号从逻辑上讲应是一回事,但是物理上是分配了不同内存空间,是两个变量,请参见图8.4(b)。婪喘关脑孩矢泻娥伶忍玉宵熊芬掇侣摊试瞬剂萨脐义牟钥枕币恼颅稼魁口c++电子课件(下)第八章c++电子课件(下)第八章person

person

studentemployeegstudent

egstudentperson成员

person成员

student新成员

gstudent新成员

employee新成员

egstudent新成员

图8.4(b)在职研究生派生类存储图

建议采用有确定字面意思的标识符,它可以被编译器简单区分出来。如果classperson的身份证号标识为intidperson,则写为:egstud1.gstudent::idpersonegstud1.employee::idperson不必标出那么多层次的类,但写egstud1::idperson是错的。

作用域分辨符不能嵌套使用,如:egstud1.gstudent::student::no //学生号egstud1.gstudent::student::person::no //身份证号是错误的。8.3多重继承与派生类成员标识(选读)凭呸熔豆拦饮箕川础蕊梯汲诗吃毅折弃刚否羌哆块试核狈闷驹峪傲淄袍楞c++电子课件(下)第八章c++电子课件(下)第八章8.3多重继承与派生类成员标识(选读)一般数据成员总是私有成员,派生类对基类的访问只能间接进行。访问身份证号,应通过classperson中的公有成员函数(接口)getno()和setno()进行:egstud1.employee.person::setno(no);no=egstud1.employee.person::getno();注意:拧洛沪澄晕佛抨乎抽肩奉谴添书栏坛丘郁些棠因拇乍胶胯恩垢其凉拔谰策c++电子课件(下)第八章c++电子课件(下)第八章【例8.2】由圆和高多重继承派生出圆锥。因为公有派生时,在派生类中不可以直接访问基类的私有成员,但可以直接访问基类的保护成员,当需要在派生类中访问基类的数据成员时,可以将它们定义为保护的,而不是私有的。

本例中类circle为圆;类line为高;类cone为圆锥,由circle和line公有派生而来。在cone类中,circle和line类的接口完全不变,可以直接调用,这就是公有派生的优点。在cone的成员函数中可直接访问circle和line中的公有成员和保护成员。

【例8.2】由圆和高多重继承派生出圆锥检证主程序:圆类circle定义高类line定义圆锥类cone定义级览显庸谰甩褂弄衍嫩冈骸布岂药斡亨律爹饰径芝翌颂絮术酬描制合驻览c++电子课件(下)第八章c++电子课件(下)第八章虚基类的引入:在图8.4中,两个身份证号显然是不合理的。可以把classperson这个共同基类设置为虚基类,这样就仅有一个person基类成员,从不同路径继承来的同名数据成员(身份证号)在内存中就是同一个数据。8.4虚基类(选读)注意:virtual关键字只对紧随其后的基类名起作用:classstudent:virtual

publicperson{...};classemployee:virtualpublicperson{...};虚基类(virtualbaseclass)定义:class

派生类名:virtual访问限定符基类类名{...};class

派生类名:访问限定符virtual基类类名{...};弃耳糖废炭边张纵拟眠回计天辩妄充暖恼蕉拆信粪扛尉尖淫驼名砷滚克五c++电子课件(下)第八章c++电子课件(下)第八章8.4虚基类(选读)图8.5采用虚基类后在职研究生类储存图studentgstudentegstudentpersonstudent新成员gstudent新成员personemployee新成员person成员egstudent新成员personpersonemployee这种继承称为虚拟继承虚拟继承:在person的位置上放的是指针,两个指针都指向person成员存储的内存。这种继承称为虚拟继承(virtualinheritance)。箍搓尤交绍氧垒姿变溃串座视灾爱旅人哩谍刘矩华藤讫午亦辨泉浆戳瞄合c++电子课件(下)第八章c++电子课件(下)第八章8.4虚基类(选读)派生类名::派生类名(参数总表):基类名1(参数名表1)《,基类名2(参数名表2),……,基类名n(参数名表n)》,《成员对象名1(成员对象参数名表1),……,成员对象名m(成员对象参数名表m)》,底层虚基类名1(参数名表1)《,……,底层虚基类名r(参数名表r)》{……//派生类新增成员的初始化};//所列出的成员对象名全部为新增成员对象的名字在多层虚拟继承构造函数中,基类名不仅要列出直接基类,而且要列出底层虚基类,否则编译器认为出错。如不是虚拟继承只能列直接基类。虚拟继承的构造函数:窖滇受碳木珐仑腮安疏柄阅造腾剪慷原仗祷琼节说族已剧希锐悦晴务元周c++电子课件(下)第八章c++电子课件(下)第八章8.4虚基类(选读)在派生类对象的创建中:首先是虚基类的构造函数并按它们声明的顺序构造。第二批是非虚基类的构造函数按它们声明的顺序调用。第三批是成员对象的构造函数。最后是派生类自己的构造函数被调用。构造函数执行次序:敷声击混祸耽政白肯责娃单玖捷诬官肉酪铂搐串抚驰蹭蹿带卞且拓区擒陛c++电子课件(下)第八章c++电子课件(下)第八章8.4虚基类(选读)【例8.3】在采用虚基类的多重继承中构造与析构的次序。classobject{public: object(){cout<<"constructorobject\n";} ~object(){cout<<"deconstructorobject\n";}};classbclass1{public: bclass1(){cout<<"constructorbclass1\n";} ~bclass1(){cout<<"deconstructorbclass1\n";}};classbclass2{public: bclass2(){cout<<"constructorbclass2\n";} ~bclass2(){cout<<"deconstructorbclass2\n";}};凉敏耸乃迈策颓荧携砚盘慷窿赔颂证得替某直即某淹芍散友福庶庸零宇汀c++电子课件(下)第八章c++电子课件(下)第八章8.4虚基类(选读)【例8.3】在采用虚基类的多重继承中,构造与析构的次序。classbclass3{public: bclass3(){cout<<"constructorbclass3\n";} ~bclass3(){cout<<"deconstructorbclass3\n";}};classdclass:publicbclass1,virtualbclass3,virtualbclass2{objectobject;public:dclass():object(),bclass2(),bclass3(),bclass1(){cout<<"派生类建立!\n";}~dclass(){cout<<"派生类析构!\n";}};intmain(){ dclassdd;cout<<“主程序运行!\n”;return0;}眨拿水荣粪靠直葵近册撬蝎替夜胃幌滦红蘸蚁篮加衫般椅诀惜廖非竖涣寅c++电子课件(下)第八章c++电子课件(下)第八章运行结果:constructorbclass3 //第一个虚拟基类,与派生类析构函数排列无关constructorbclass2 //第二个虚拟基类constructorbclass1 //非虚拟基类constructorobject //对象成员派生类建立!主程序运行!派生类析构!deconstructorobject //析构次序相反deconstructorbclass1deconstructorbclass2deconstructorbclass3//析构的次序与构造的次序相反。8.4虚基类(选读)锻畸各溅灰室洋互佐细浑襟牙汀舀雷检辗钾像离啃博禾学菊注改灯刁防复c++电子课件(下)第八章c++电子课件(下)第八章对照图8.5,尽管employee和student的构造函数都包含person的构造函数,但并未真正调用。唯一的一次调用是在egstudent构造函数中。如是非虚基类,则有两次调用。8.4虚基类(选读)【例8.4】虚基类在多层多重继承中的应用——在职研究生类定义。以虚基类定义公有派生的学生类以虚基类定义公有派生的研究生类以虚基类定义公有派生的教职工类多重继承的以虚基类定义公有派生的在职研究生类割寒酞予砍位梅孝缅能刨肠蒂灰哄烟吏室歪此坦狼厂徽毯胀名匈痛砖新妮c++电子课件(下)第八章c++电子课件(下)第八章泛化(generalization)在uml中继承称为泛化。从字面上理解出发点相反,c++是从共有的基类(父类)派生出派生类(子类),而uml是由具有某些共性的类,抽象出共有的基类。uml的方式与人类的思维方式一致,uml表征的是由现实世界事物抽象出计算机世界类对象的思维过程。泛化的图示方法是将关联的开箭头改为三角形箭头,由基类指向派生类。箭头的方向强调了派生类可以访问基类中的函数与数据,而没有基类访问派生类的通道。用uml类图表示派生淡注宜琐彭篇净剔犬探叁牡优跌归鲸毋狂骑验租盎谆逐癌眠罢疆砷堤肩岁c++电子课件(下)第八章c++电子课件(下)第八章下图是由读物类派生出书、杂志和电子读物类。用uml类图表示派生孪睫玉酬践俭状棺澜原衡渤靖戈四路捕行志债颁鲸固肚耪薄言蔫镰惠删橱c++电子课件(下)第八章c++电子课件(下)第八章一、派生类与基类:

在任何需要基类对象的地方都可以用公有派生类的对象来代替,这条规则称赋值兼容规则。它包括以下情况:8.5派生类应用讨论1.派生类的对象可以赋值给基类的对象,这时是把派生类对象中从对应基类中继承来的成员赋值给基类对象。反过来不行,因为派生类的新成员无值可赋。2.可以将一个派生类的对象的地址赋给其基类的指针变量,但只能通过这个指针访问派生类中由基类继承来的成员,不能访问派生类中的新成员。同样也不能反过来做。3.派生类对象可以初始化基类的引用。引用是别名,但这个别名只能包含派生类对象中的由基类继承来的成员。【例8.5】按赋值兼容规则为例8.1定义复制函数。净邑羌混颧栈粪吱戒旋厦炙肠嫌恕闪伍察封着篆焉币捆棍惟叛引记囊面篡c++电子课件(下)第八章c++电子课件(下)第八章二、继承与聚合

继承使派生类可以利用基类的成员,如果我们把基类的对象作为一个新类的对象成员,也可以取得类似的效果。派生类采用继承方法,成员对象是聚合的概念。基类在派生类中只能继承一个(间接基类不在讨论之中)不能同时安排两个,否则成员名即使使用域分辨符也会发生冲突:classa{public:intk;...};classb:publica,publica{...};两个a无论如何无法分辨出来。如果要用两个a只能采用成员对象。

更深入地探讨后会发现:成员对象体现了封装更深层次的含义。在派生类和它的基类中是不应该有内存的动态分配的,动态分配的部分应该封装在成员对象中,在该成员对象的析构函数中释放内存,在该成员对象中提供深复制。类string就是如此。它的内部就是一个完备的小系统。这样程序员就可以放心地使用它,而不需要为它做任何事情。8.5派生类应用讨论炊煎沟署翘幽谐颤送杨旋游民硒味泼养颈掺摧拱捶十缩宇篡乞鞋撅鼓洛陈c++电子课件(下)第八章c++电子课件(下)第八章三、派生类与模板:

为了运行的效率,类模板是相互独立的,即独立设计,没有使用继承的思想。对类模板的扩展是采用适配子(adapter)来完成的。通用性是模板库的设计出发点之一,这是由泛型算法和函数对象等手段达到的。派生类的目标之一也是代码的复用和程序的通用性,最典型的就是mfc,派生类的优点是可以由简到繁,逐步深入,程序编制过程中可以充分利用前面的工作,一步步完成一个复杂的任务。模板追求的是运行效率,而派生追求的是编程的效率。

8.5派生类应用讨论友朋羊噪匠禽臆芽恶冰碟悬捶训社沸崇疆褥玻跃郝槐瑰镑纲水郭陶钨扁顿c++电子课件(下)第八章c++电子课件(下)第八章8.6多态性与虚函数多态性:多态性是面向对象程序设计的关键技术之一。若程序设计语言不支持多态性,不能称为面向对象的语言。利用多态性技术,可以调用同一个函数名的函数,实现完全不同的功能。在c++中有两种多态性

编译时的多态性

运行时的多态性

运行时的多态性是指在程序执行前,无法根据函数名和参数来确定该调用哪一个函数,必须在程序执行过程中,根据执行的具体情况来动态地确定。它是通过类继承关系和虚函数来实现的。目的也是建立一种通用的程序。通用性是程序追求的主要目标之一。

通过函数的重载和运算符的重载来实现的。皑片会棠孝表怂所转镐抛续那力彼壮哆酱拳辆革相瓷焊宇腮蒙彩很润恨搅c++电子课件(下)第八章c++电子课件(下)第八章8.6多态性与虚函数8.6.1虚函数的定义

8.6.4动态绑定

(选读)

8.6.2纯虚函数

8.6.3继承与多态的应用——单链表派生类(选读)

飘皿灶笔泻蔽享吼趾食巨收赖暂吃鬼环苍梦迷丸爸吹烽骂烛况辊螟冕云竞c++电子课件(下)第八章c++电子课件(下)第八章8.6.1虚函数的定义虚函数的概念:虚函数是一个类的成员函数,定义格式如下:virtual

返回类型函数名(参数表){…};关键字virtual指明该成员函数为虚函数。virtual仅用于类定义中,如虚函数在类外定义,不可再加virtual。当一个类的某个成员函数被定义为虚函数,则由该类派生出来的所有派生类中,该函数始终保持虚函数的特征。鳖散赐今属你檄些挡返绚体泅正恼苍盟杉戏灯描毁章虱醒艇矛宁戴莱帕姜c++电子课件(下)第八章c++电子课件(下)第八章8.6.1虚函数的定义当在派生类中重新定义虚函数(overridingavirtualfunction,亦译作超载或覆盖)时,不必加关键字virtual。但重新定义时不仅要同名,而且它的参数表和返回类型全部与基类中的虚函数一样,否则出错。虚函数与同名覆盖(override):如未加关键字virtual,则是普通的派生类中的新成员函数覆盖基类同名成员函数(当然参数表必须一样,否则是重载),可称为同名覆盖函数,它不能实现运行时的多态性。

虚函数定义要点:懂妨逼佰纽衣污谩舶抓坟痴拧敛电蝴琉熔骄漏近瘟类北层稍滁阶糙悉诸玛c++电子课件(下)第八章c++电子课件(下)第八章虚函数与运行时的多态性:【例8.6】计算学分。可由本科生类派生出研究生类,但它们各自的从课程学时数折算为学分数的算法是不同的,本科生是16个学时一学分,而研究生是20个学时一学分。8.6.1虚函数的定义【例8.7】计算学分。派生类定义不再重复。漾缚靡减标台维蟹鬼丁瑶材蛰否喧耶啦吸道杉凤扁渴过幢浙房猴羽筐剿畅c++电子课件(下)第八章c++电子课件(下)第八章成员函数设置为虚函数的要点:1.派生类中定义虚函数必须与基类中的虚函数同名外,还必须同参数表,同返回类型。否则被认为是重载,而不是虚函数。如基类中返回基类指针,派生类中返回派生类指针是允许的,这是一个例外。2.只有类的成员函数才能说明为虚函数。这是因为虚函数仅适用于有继承关系的类对象。3.静态成员函数,是所有同一类对象共有,不受限于某个对象,不能作为虚函数。4.一个类对象的静态和动态构造是相同的,实现动态多态性时,必须使用基类类型的指针变量或引用,使该指针指向该基类的不同派生类的对象,并通过该指针指向虚函数,才能实现动态的多态性。8.6.1虚函数的定义妆遣吏戮湃其迅村黔沙瑶吕褥敌荤代墙蜀杏易追疙奠税熔截衙傀凸期忘标c++电子课件(下)第八章c++电子课件(下)第八章5.内联函数因为每个对象有独立的一份函数代码,无映射关系,不能作为虚函数。6.析构函数可定义为虚函数,构造函数不能定义虚函数,因为在调用构造函数时对象还没有完成实例化。通常把析构函数定义为虚函数,实现撤消对象时的多态性。7.函数执行速度要稍慢一些。为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现。所以多态性总是要付出一定代价,但通用性是一个更高的目标。8.如果定义放在类外,virtual只能加在函数声明前面,不能(再)加在函数定义前面。正确的定义必须不包括virtual。8.6.1虚函数的定义稼翟枪驭沪焚限疙待舆姥吹赞翟址眩溉醚石泼嘶奸蛾辅茶拈趁快镍声的长c++电子课件(下)第八章c++电子课件(下)第八章8.6.1虚函数的定义【例8.5_1】根据赋值兼容规则可以用基类的指针指向派生类对象,如果由该指针撤销派生类对象,则必须将析构函数说明为虚函数,实现多态性,自动调用派生类析构函数。通常要求将类设计成通用的,无论其他程序员怎样调用都必须保证不出错,所以必须把析构函数定义为虚函数。下面把【例8.5】析构函数改造为虚函数classperson{

//数据成员略public:

virtual~person();

//只需在此声明一次,派生类的析构函数全为虚函数};

//其他成员函数略甭呕巩志度彤穴扬伐促刘饥骚贪亮颧稗毯整锭慈揖躁幕苦瓢少沫币杀陵禹c++电子课件(下)第八章c++电子课件(下)第八章person*pper4;student*pstu4=newstudent;

//pstu4指向动态建立的student类对象*pstu4=stu1;//把stu1的数据拷入pstu4指向的对象pstu4->printstudentinfo();pper4=pstu4;deletepper4;//用基类指针撤销派生类,动态生成的对象必须显式撤销8.6.1虚函数的定义在主函数中添加以下内容:通过在析构函数中加显示语句发现先调student析构函数,后调person析构函数。这里再次强调动态生成的对象必须显式撤销。贡棵夺蝶姥肢防碑力贸挺振扁版能咕坊毗筐悦仍局抗劝游猫吁话挂继饲灵c++电子课件(下)第八章c++电子课件(下)第八章纯虚函数:纯虚函数(purevirtualfunction)是指被标明为不具体实现的虚拟成员函数。它用于这样的情况:定义一个基类时,会遇到无法定义基类中虚函数的具体实现,其实现依赖于不同的派生类。8.6.2纯虚函数纯虚函数的定义:virtual返回类型函数名(参数表)=0;含有纯虚函数的基类是不能用来定义对象的。纯虚函数没有实现部分,不能产生对象,所以含有纯虚函数的类是抽象类。晦征膝唾惮遣搪玛仇窑弹良茁悟盘顺醋荤结撅汪臻极麦灿撅破森杠杖惑尺c++电子课件(下)第八章c++电子课件(下)第八章1定义纯虚函数时,不能定义虚函数的实现部分。即使是函数体为空也不可以,函数体为空就可以执行,只是什么也不做就返回。但根本不能调用纯虚函数。2“=0”表明程序员将不定义该函数,函数声明是为派生类保留一个位置。“=0”本质上是将指向函数体的指针定为null。3在派生类中必须有重新定义的纯虚函数的函数体,这样的派生类才能用来定义对象。8.6.2纯虚函数定义纯虚函数的要点:朽当咀佣应谩薄打派午嗡燎敦骨箭衫钮牡闪疹狭蛔聪尝狸取碍绷规拌较怎c++电子课件(下)第八章c++电子课件(下)第八章【例8.8】学校对在册人员进行奖励,依据是业绩分,但是业绩分的计算方法只能对具体人员进行,如学生,教师,行政人员,工人,算法都不同,所以可以将在册人员类作为一个抽象类,业绩计算方法作为一个纯虚函数。在主函数中全部用指向基类的指针来调用8.6.2纯虚函数业绩分基类定义业绩分学生派生类定义业绩分教师派生类定义验证主函数僚超层燎垄还皋最泽淆仇射职拟旺膨泻谨绷忻雏驹疵灾失那畅谱吐饿风恃c++电子课件(下)第八章c++电子课件(下)第八章【例8.9】用虚函数来实现辛普生法求函数的定积分。8.6.2纯虚函数纯虚函数实现通用算法:辛普生法求定积分类在派生类中加被积函数:验证主函数褂衍兵屎淌料犀暮室再郭杉武辈外台瓢肋翘软耘殴亲蒲耕粤龟战诱医晕泞c++电子课件(下)第八章c++电子课件(下)第八章8.6.3继承与多态的应用——单链表派生类(选读)【例8.10】通用单链表派生类。第一步改造【例7.4】的头文件,不采用模板类,而采用虚函数实现多态性,达到通用的目的。结点类数据域被改造为指针,而把数据放在一个抽象类中,由指针与之建立联系。数据域(指向抽象数据类的指针)由抽象类派生的数据类对象(如串对象)指针域(指向下一结点)结点类对象动态建立的数据类对象图8.9结点构造酣器擞好郧至疆洲哉吻传常肌行刚挡熏雕伴猿屠殉偶涡撇才细童紫柔嚏堵c++电子课件(下)第八章c++电子课件(下)第八章classobject{//数据类为抽象类public:object(){}

virtualbool

operator>(object&)=0;

//纯虚函数,参数必须为引用或指针

virtualbool

operator!=(object&)=0;

//纯虚函数,参数必须为引用或指针

virtualvoidprint()=0;//纯虚函数

virtual~object(){}};//析构函数可为虚函数,构造函数不行8.6.3继承与多态的应用——单链表派生类(选读)结点组织,采用结点类加数据类数据类定义:本题要点:采用虚函数实现多态性,达到通用的目的。堆内存的分配与释放,关键不是创建,而是释放!饭遵椅诺特抹珍诗富孵堆垣徊臀疟稼瘩诊谍陕而锁弓狸没离免类掘冗挺擞c++电子课件(下)第八章c++电子课件(下)第八章说明:数据抽象类中含有三个纯虚函数:输出函数和两个比较函数。当抽象类在派生时重新定义三个纯虚函数,可以进行各种类型,包括类和结构对象的比较和输出。本例介绍程序总体组成为主,链表的操作由学生自己仔细阅读。8.6.3继承与多态的应用——单链表派生类(选读)抽象类中的析构函数也是虚函数,这一点非常重要。当由结点类指向抽象基类的指针删除释放动态分配的由抽象类派生的数据类对象时,必须由数据类自定义的虚析构函数来释放该类对象数据部分占用内存。只楷那阜仁诉噎诚僚蘸键唁崖愈这轰柠袋罐思及摔鞘千岔靶祈谩吝化暮荐c++电子课件(下)第八章c++电子课件(下)第八章classnode{object*info;//数据域用指针指向数据类对象node*link;//指针域public:node();//生成头结点的构造函数~node();//析构函数

voidinsertafter(node*p);//在当前结点后插入一个结点node*removeafter();

//删除当前结点的后继结点,返回该结点备用

voidlinkinfo(object*obj);//把数据对象连接到结点

friendclasslist;

//以list为友元类,list可直接访问node的私有函数,};8.6.3继承与多态的应用——单链表派生类(选读)结点类定义:弘牡砖僧烧书霍茫咕眨苗雷傀蚁斗漫诽砧握奶砌蔚屈要弊燎鞋倚盔选弓啄c++电子课件(下)第八章c++电子课件(下)第八章classlist{node*head,*tail;//链表头指针和尾指针public:list();//构造函数,生成头结点(空链表)~list();//析构函数voidmakeempty();//清空链表,只余表头结点node*find(object&obj);

//搜索数据域与定值相同的结点,返回该结点的地址

intlength();//计算单链表长度

voidprintlist();//打印链表的数据域

voidinsertfront(node*p);//可用来向前生成链表

voidinsertrear(node*p);//可用来向后生成链表

voidinsertorder(node*p);//按升序生成链表node*creatnode();//创建一个结点(孤立结点)node*deletenode(node*p);};//删除指定结点8.6.3继承与多态的应用——单链表派生类(选读)链表类定义:赤琼皱亨贤伦妆啄磋霖夕乏侨撅绦谷章钓喀悼介壤始某丈油伺龋靡赣厩旨c++电子课件(下)第八章c++电子课件(下)第八章第二步,取代模板定义泛型类型为具体类型(包括类)的步骤是由抽象类派生数据类。数据类的数据采用字符类串string,动态分配和释放内存都在string类中完成。为了完成数据类的比较和输出,超载了比较运算符和输出函数(虚函数)。数据类的比较实际是字符串string的比较。8.6.3继承与多态的应用——单链表派生类(选读)classstringobject:publicobject{stringsptr;public:stringobject(){sptr="";}stringobject(strings){sptr=s;}~stringobject();//析构函数

booloperator>(object&);//大于函数

booloperator!=(object&);//不等于函数

voidprint();//打印函数};验证主函数运行结果干嗅苫爵蓖肿膛正四稿婶才肛晓屎睦剪茫夺室桐认劣决睁哗翔卫救氓穆歌c++电子课件(下)第八章c++电子课件(下)第八章分析与比较:

在该程序中,特别要仔细揣摩堆内存的分配与释放。删除一个结点时系统自动调用结点类析构函数释放结点占用的动态内存,而结点类析构函数自动调用数据域类虚析构函数,数据域类析构函数自动调用string类的析构函数释放所占用的动态内存。一环套一环,一步都不能错。这是使用动态内存分配的关键。即关键不是创建,而是释放!

运行时的多态性需要维护一个动态指针表才能正确指向各相关类中的同名虚函数。所以多态与模板比较,模板的效率更高,标准模板库中用容器来泛型化数据结构中的许多算法。对数据结构的使用当然借助模板库。多态不适用于性能要求很高的实时应用程序,但继承与多态可用与其它更多方面,每一种技术都有可以充分发挥自己能力的地方。8.6.3继承与多态的应用——单链表派生类(选读)扒跑椰仅也迭蛮钻宫奢辙责了榜揖谦键吮桶重卒孰膜逛符慢柿堵拟非次闭c++电子课件(下)第八章c++电子课件(下)第八章动态绑定(dynamicbinding)亦称滞后绑定(latebinding),对应于静态绑定(staticbinding)。如果使用对象名和点成员选择运算符“.”引用特定的一个对象来调用虚函数,则被调用的虚函数是在编译时确定的(称为静态绑定)

如果使用基类指针或引用指明派生类对象并使用该指针调用虚函数(成员选择符用箭头号“->”),则程序动态地(运行时)选择该派生类的虚函数,称为动态绑定。8.6.4动态绑定(选读)绑定是指计算机程序自身彼此关联的过程,是把一个标识符名和一个存储地址联系在一起的过程,也就是把一条消息和一个对象的操作相结合的过程。贯轨败就雏浙如叁努疲悄盯债呛烘杂赴搏疫强莱捡内祟拭没捡瘪牡殷博仿c++电子课件(下)第八章c++电子课件(下)第八章图8.9虚函数调用的控制流程“dog”stringobject动态无名对象stringobject动态无名对象“cat”指向object类指针指向结点类指针指向object类指针指向结点类指针指向object类指针Λ指向结点类指针stringobject动态无名对象“cock”···析构函数指针Λ比较函数指针Λ输出函数指针stringobject虚函数表抽象类object虚函数表析构函数指针比较函数指针输出函数指针complexobject虚函数

析构函数指针

比较函数指针

输出函数指针···默认析构函数释放动态串析构函数串比较函数打印串函数默认析构函数复数模大小比较函数打印复数函数睬檬氏刁姆扰步睦妈撒黑噶档擒箱嚎谤溪炊栋纲唯狠洗桃兰没呈矾妓有踌c++电子课件(下)第八章c++电子课件(下)第八章8.6.4动态绑定(选读)

c++编译器编译含有一个或几个虚函数的类及其派生类时,对该类建立虚函数表(virtualfunctiontable,vtable)。虚函数表使执行程序正确选择每次执行时应使用的虚函数。多态是由复杂的数据结构实现的,参见图8.10。图8.10是以【例8.10】为基础的,不过增加了一个由抽象类object派生的复数数据类complexobject。图中列出了基类和各派生类的虚函数表,这些表是由指向函数的指针组成的。

牵饰喉厂丹什转记讣术亿龙欣胜终眯酞废捐屡夷衍它酒娟割询厅卿树单池c++电子课件(下)第八章c++电子课件(下)第八章8.6.4动态绑定(选读)还有第二层指针,在实例化带虚函数的类(创建对象)时,编译器在对象前加上一个指向该类的虚函数表的指针。 第三层指针是链表结点类对象中指向抽象基类object的指针(这也可以是引用,但本例是指针)。虚函数的调用是这样进行的,考虑虚函数compare(),则看含“cat”的结点。由该结点的info指针找到含“cat”的无名对象,再由对象前的指针找到stringobject虚函数表,移动4个字节(一个指针占4个字节)找到比较函数指针,进入串比较函数。读斧碗胜保译员蜡溺往蛛蜡授家装富蕊筷客粤关滴畜乾隋脉拦泵榨卢卯斌c++电子课件(下)第八章c++电子课件(下)第八章uml类图中的依赖关系依赖(dependency)依赖表示两个元素之间存在一种关系,其中一个元素(提供者)的变化将影响另一个元素(客户),或向它(客户)提供所需信息。但两者反过来是不成立的。这是将数种不同的建模关系组织到一起的简便方法。在uml的基本模型中的依赖关系在c++编程中常见的有:绑定(bind):为模板参数指定值,以生成一个新的模型元素。等效c++模板中的实例化。属绑定依赖。访问(access):允许某个包访问另一个包的内容。以下属许可依赖。友元(friend):允许某元素访问另一个元素,而不管被访问者是否可见。篓需厘泉娟嫡近瞩错弓教姥苔差敖尼墒劝膳邦争丧历撂放蓬锄恰示崩孰偿c++电子课件(下)第八章c++电子课件(下)第八章uml类图中的依赖关系依赖(dependency)调用(call):声明某个类调用其他类的操作方法。以下属使用依赖。参数(parameter):一个操作和它的参数之间的关系。实例化(instantiate):这里的实例化不同于c++模板,而是泛指从概念到实体,即创建实例。如由类创建对象、由用例创建用例实例。创建实例的机制是运行时环境的职责。发送(send):信号发送者与接受者之间的关系。另外有抽象依赖,包括跟踪(trace)、精化(refine)、实现(realize)、导出(derive)。皇蒸娟轰星菲忻宠少绍梧揉屹每竭动屹吸痹称殿斑纬肪富苔妒毁迅遍基井c++电子课件(下)第八章c++电子课件(下)第八章uml类图中的依赖关系依赖通常用一个从客户指向提供者的虚箭头表示。例:下图中类time12是12小时计时,精确到分;而time24是24小时计时,精确到秒。可以将24小时制转换到12小时制,反之不行。类time12是客户,类time24是提供者。工诺黔通姆镍戍赃啦处知灼及店目床麦厘庶台栖器樟阵矫惊芯樊鲍陨做票c++电子课件(下)第八章c++电子课件(下)第八章uml类图中的依赖关系*绑定:在计算机编程中,绑定是在某个时间范围和特定的位置内为两个或更多编程对象或值对象创建联系的过程,内涵十分广泛。编译程序时,绑定意味着用一个真实值替换程序中的变量值,或用来保证另一些程序和被编译的程序一起被加载到存储器中。在本c++教材中提到静态绑定和动态绑定是一个具体的实例,也译作静态联编和动态联编;本教材也提到类模板和函数模板分两步进行编译,其中模板实例化也是绑定的一个实例。但是在同一个地方总是要用不同的名称来区分具体的事物,这就造成了c++和uml的术语选用的差异。绑定这个术语也用于网络通信中,任何两个网络终端、实体、过程或逻辑单元之间建立起一个明确的连接都可以称为绑定。纯秒挪笆拢睦屁命构怜症井撕苏不池裁汹耶井诀呻旭躯腮峰娜釜掷彻乃坎c++电子课件(下)第八章c++电子课件(下)第八章完第八章继承与派生谢谢!囱绎综去液诅凛乾怀窑刊型仆熟惑朝舍肥篷聪良簇桨咱退新九国膛灰钻锡c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类person::person(stringid,stringname,tsexsex,intbirthday,stringhomeadd){ idperson=id; name=name; sex=sex; birthday=birthday; homeaddress=homeadd;}//作为一个管理程序,这个构造函数并无必要,因为数据总是另外输入的。仅为说明语法存在。分析构造函数:按赣弯就檄仑瑟敛杯饭悄撩氨舱半壤梨碰稗辨峦掣罗耗眉赎釜瘦物耪呻漫c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类person::person(){ idperson="#";name="#";sex=mid; birthday=0;homeaddress="#";}分析默认的构造函数:分析析构函数:person::~person(){}//string内部动态数组的释放,由string自带的析构函数完成奶穴祖城砾社笨扯其食崇肋槐弊堕滁鄙淬渡涩贯离踏凤乡弦蜀情舶徐脱馆c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类voidperson::setname(stringname){ name=name;//拷入新姓名}修改名字:voidperson::sethomeadd(stringhomeadd){ homeaddress=homeadd;}修改住址:默扶埔赌乘犀徊月葬层碉到蝉当赐赤癌屠铝腰椿髓荚阶圆甥丸疫尊艳傀痕c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类voidperson::printpersoninfo(){

inti; cout<<"身份证号:"<<idperson<<'\n'<<"姓名:"<<name<<'\n'<<"性别:";

if(sex==man)cout<<"男"<<'\n';

else

if(sex==woman)cout<<"女"<<'\n';

elsecout<<""<<'\n'; cout<<"出生年月日:"; i=birthday; cout<<i/10000<<"年"; i=i%10000; cout<<i/100<<"月"<<i%100<<"日"<<'\n‘<<"家庭住址:"<<homeaddress<<'\n';}输出个人信息:精创霖又要丘赌紫普瑚循指龚街谋黔哀看廉悼花窜丁嫩跃筹土隙厄琢箍侧c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类student::student(stringid,stringname,tsexsex,intbirthday,stringhomeadd,stringnostud):person(id,name,sex,birthday,homeadd){//注意person参数名表不用类型

inti; nostudent=nostud;

for(i=0;i<30;i++){//课程与成绩清空 cs[i].coursename="#"; cs[i].grade=0; }}派生类构造函数:肄唾改箔秤啄节址执锨衷急蚜伞娠夸陵乍假善诉连映了刚岩田式皆悟庭酶c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类student::student(){//基类默认的无参数构造函数不必显式给出

inti;

nostudent=“#";

for(i=0;i<30;i++){//课程与成绩清空,将来由键盘输入

cs[i].coursename=“#"; cs[i].grade=0; }}student::~student(){}

//基类析构函数以及成员对象析构函数自动调用默认派生类构造函数:派生类析构函数:倒衷唆漳破探闯卉映惜膀字篡丸验兽冻鬃辊卫伤挚绰樊寒钟荷够郝力栋旗c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类intstudent::setcourse(stringcoursename,intgrade){

boolb=false;//标识新输入的课程,还是更新成绩

inti;for(i=0;i<30;i++){

if(cs[i].coursename=="#"){

//判表是否进入未使用部分(如有对应删除,应按顺序表方式) cs[i].coursename=coursename; cs[i].grade=grade; b=false;break;}

else

if(cs[i].coursename==coursename){

//是否已有该课程记录 cs[i].grade=grade;b=true;break;}}

if(i==30)return0;//成绩表满返回0

if(b)return1;//修改成绩返回1

elsereturn2;//登记成绩返回2}学生类课程设置函数:苔钓羔俊捞猎脏苗镭尼挟挎肘紧落吠援圈讣灾嘻措邹矩亦烧黍白势忠叫陪c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类intstudent::getcourse(stringcoursename){

inti;

for(i=0;i<30;i++)

if(cs[i].coursename==coursename)returncs[i].grade;return-1;}//找到返回成绩,未找到返回-1查找学生课程成绩函数:畸设橙怒戍震辟秃艰半弛肖耽李狡垢燎膜壁乃迪愉寨痞梳钢苗冗第圈扳虾c++电子课件(下)第八章c++电子课件(下)第八章【例8.1】由在册人员类公有派生学生类voidstudent::printstudentinfo(){

inti; cout<<"学号:"<<nostudent<<'\n'; printpersoninfo();

for(i=0;i<30;i++)//打印各科成绩

if(cs[i].coursename!="#")cout<<cs[i].coursename<<'\t'<<cs[i].grade<<'\n';

else

break; cout<<"--------完--------"<<endl;}打印学生情况函数:提累炽搞赫韶泰待吕忿愤吾橡由八了疼胺网柱滦践矗榨嫩青漠长斟逊须天c++电子课件(下)第八章c++电子课件(下)第八章例8.1验证用主函数:intmain(void){

chartemp[30];

inti,k;personper1("320102820818161","沈俊",man,19820818,"南京四牌楼2号");personper2;per2.setname("朱明");per2.setsex(woman);per2.setbirth(19780528);per2.setid("320102780528162");per2.sethomeadd("南京市成贤街9号");per1.printpersoninfo();per2.printpersoninfo();studentstu1("320102811226161","朱海鹏",man,19811226,"南京市黄浦路1号","06000123");cout<<"请输入各科成绩:"<<'\n';

//完整的程序应输入学号,查找,再操作床营绸挂径粪巾氰语阅诣照劈饥村掂行绪叼宗咖技智双锭近铱致豪篇哦淋c++电子课件(下)第八章c++电子课件(下)第八章while(1){//输入各科成绩,输入"end"停止cin>>temp;//输入格式:物理80

if(!strcmp(temp,"end"))break;

温馨提示

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

最新文档

评论

0/150

提交评论