《C++面向对象程序设计》教学内容第1章C++概述第2章类和对象第3章面向对象程序设计概述第4章进一步学习类和对象第5章堆与复制构造函数第6章继承性:派生类第7章运算符重载第8章虚函数和多态性第9章模板第10章类库和C++的标准模板库STL第11章输入输出流第12章异常处理第6章继承性:派生类6.1派生类的概念6.2单继承6.3派生类的构造函数和析构函数6.4多重继承6.5赋值兼容规则6.6应用举例一个现有的类ClassStudent{intnumber;char*name;floatscore;public:Student(intnumber1,char*name1,floatscore1);voidmodify(floatscore1);voidprint();};在现有类的基础上创建新类:派生classUstudent:publicStudent{charmajor;//专业public:Ustudent(intnumber1,char*name1,char*major,floatscore1);voidprint();};//Ustudent为大学生类派生类基类6.1派生类的概念•保持已有类的特性而构造新类的过程称为继承(inheritance)。(OOP术语)•在已有类的基础上新增自己的特性而产生新类的过程称为派生(derive)。(C++术语)•被继承的已有类称为基类(或父类)。•派生出的新类称为派生类(或子类)。类继承的一个经典例子classshape{public:floatarea(){return-1;}};classcircle:publicshape{floatradius;public:circle(floatr){radius=r;}floatarea(){return3.14158*radius*radius;}};shapecircle类继承的一个经典例子(续)classrectangle:publicshape{floatlength,width;public:rectangle(floatl,floatw){length=l,width=w;}floatarea(){returnlength*width;}};shaperectangle从同一个基类可以派生出多个新类shapecirclerectangle类的继承层次与分类结构圆矩形几何形状继承与派生的目的•继承的目的:实现代码重用。•派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行扩充和改造。•C++中类的继承层次自然地表达了人们分析问题时所用的分类结构。大大改善了软件系统的可理解性和可维护性。派生类声明的一般形式class派生类名:访问控制符基类名1,….{成员定义;};多继承:一个派生类有多个基类;单继承:一个派生类只有一个基类。基类的访问控制•基类的访问说明符必须下列是三者之一:public(公有),private(私有),protected(保护)。•基类的访问控制符决定了派生类成员以及对象对继承来的基类成员的访问权限。•不同的访问控制影响主要体现在:1、派生类成员函数对基类成员的访问。2、其他函数对基类成员的访问。公有继承(inheritaspublic)公有派生(derivedaspublic)•当被继承的基类的访问说明符是public的时候,所有基类的公共成员都成为派生类的公共成员。•其他函数只能访问基类的public成员。•派生类中的成员函数可以直接访问基类中的public,但不能访问基类的private成员。一个有争议的问题•无论基类被怎样继承,基类中的私有成员都不能被其派生类成员直接访问。只能通过基类的公共接口访问。•继承来的特性不能直接使用似乎于理不通,但是派生类不加限制的访问基类的私有成员,将破坏基类的封装性,这是面向对象方法中有争议的问题之一。(两难问题)protected关键字•除了基类的被保护成员对于基类的任何派生类成员都是可访问的以外,protected访问说明符与private说明符是完全等效的。•派生类的成员函数不能直接访问基类的私有(private)成员。但派生类的成员函数能访问基类的被保护(protected)成员。保护继承(inheritasprotected)•基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可访问。•派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。•派生类的对象不能访问基类中的任何成员。私有继承(inheritasprivate)私有派生(derivedasprivate)•基类的所有public和protected成员都成为派生类的私有成员。•派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。•其他函数不能通过派生类的对象访问基类中的任何成员。6.2单继承•派生类只有一个基类的时称为单继承。pointline单继承的例子#includeiostreamusingnamespacestd;classpoint{charc;public:point(charch){c=ch;}voidDrawPoint(){coutc;}};单继承的例子(续1)classline:publicpoint{intlength;public:line(charch,inti):point(ch){length=i;}voidDrawLine(){length=(length0?0:(length80?80:length));for(inti=0;ilength;i++)DrawPoint();coutendl;}};单继承的例子(续2)intmain(){linemy_line('*',30);my_line.DrawLine();coutendl;return0;}pointlinemy_line派生类的对象中有一个无名的基类对象单继承时类层次为树形结构工具车轿车面包车小汽车卡车旅行车汽车交通工具6.3派生类的构造函数和析构函数•首先,基类和派生类的构造函数与析构函数在什么时候被调用?•第二,如何将参数传递给基类的构造函数。构造函数与析构函数的调用次序•先调用基类的构造函数,后调用派生类的构造函数;•先调用派生类的析构函数,后调用基类的析构函数;将参数传递给基类的构造函数•问题:派生类中包含的无名对象要初始化。•解决方案:•用派生类的构造函数激活基类的构造函数。line(charch,inti):point(ch){length=i;}派生类的构造函数和析构函数的例子classpoint{charc;public:point(charch){c=ch;}voidDrawPoint(){coutc;}};classline:publicpoint{intlength;public:line(charch,inti):point(ch){length=i;}voidDrawLine(){length=(length0?0:(length80?80:length));for(inti=0;ilength;i++)DrawPoint();coutendl;}};一个参数传递链:将参数传递给基类的构造函数派生类的构造函数和析构函数的例子(续)classrectangle:publicline{intwidth;public:rectangle(charch,inti,intj):line(ch,i){width=j;}voidDrawRect(){width=(width0?0:(width25?25:width));for(inti=0;iwidth;i++)DrawLine();}};一个参数传递链:将参数传递给基类的构造函数派生类的构造函数和析构函数的例子(续)intmain(){rectanglerect('X',40,12);rect.DrawRect();coutend;return0;}多层继承•所谓多层继承是指从一个类派生出一个类,然后又从该派生类派生出另一个类,如此进行下去,直到最后生成的类满足要求为止。•例如:从基类point派生出line(线),然后从line派生出rectangle(矩形)。pointlinerectangle继承是重用的基础•面向对象的程序设计中通过继承实现重用reuse——便于重用,扩充。•可以在原有的代码(程序)基础上扩充,不修改原有代码。•即使原来的代码完全不符合当前的要求,也不必修改原有的代码。可以只增加新的代码来实现新的功能。函数隐藏(functionhide)•函数隐藏:如果我们在派生类中重新定义一个函数,其函数原型与基类中的某个函数完全相同,那么基类中的对应函数在派生类中就被隐藏了,我们称之为函数隐藏。(例10)•使用这种方式可以改造软件功能而不修改原来的源代码。函数隐藏的例子#includeiostream.h#includestring.hclassperson{charname[10];intage;public:person(){age=0;}voidset(inti,char*str){age=i;strcpy(name,str);}voiddisplay(){coutnameisageyearsold.endl;}};函数隐藏的例子(续)classstudent:publicperson{intmath,chinese,english;public:student(){math=chinese=english=0;}voidSetScore(inti,intj,intk){math=i;chinese=j;english=k;}voiddisplay(){coutThestudent'sscoreis:endlmath:mathendlchinese:chineseendlenglish:englishendl;}};函数隐藏的例子(续)intmain(void){studentst;st.set(15,Wang);st.SetScore(88,85,90);st.person::display();st.display();return0;}支配规则•派生类的对象引用某函数时,编译器先在派生类中找与该函数完全匹配的函数,有则调用之;•否则到其基类中找,依次再到基类的基类中找,……等等,若都没有找到则出错。•若第一次遇到了同名函数但参数不匹配,则对其进行类型转换后再调用之,若无法转换则出错。6.4多继承InheritingMultipleBaseClasses从两个以上的基类派生出新类,例如:class派生类名:继承方式1基类名1,继承方式2基类名2,...{派生类的成员定义;}注意:每一个“继承方式”,只用于限制对紧随其后之基类的继承。输入输出流类库的结构流类的继承层次iosostreamistreamclogfstreamiostreamcerrcinifstreamofstreamcoutiostream.h多继承举例classA{public:voidsetA(int);voidshowA();private:inta;};classB{public:voidsetB(int);voidshowB();private:intb;};classC:publicA,privateB{public:voidsetC(int,int,int);voidshowC();private:intc;};voidA::setA(intx){a=x;}voidB::setB(intx){b=x;}voidC::setC(intx,inty,intz){setA(x);//派生类成员直接访问s