




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C++ProgrammingLanguageDr.ZhengXiaojuanAssociateProfessorSoftwareCollegeofNortheastNormalUniversityOctober.20081C++ProgrammingLanguage1
第七章继承与派生
2
第七章继承与派生2
本章内容
1继承与派生2类的继承方式3多继承4派生类的构造和析构函数5派生中成员的标识与访问虚基类基类私有成员的访问引入派生类后的对象指针3本章内容1继承与派生3在C++中,给我们提供了一种重要的机制,就是继承。理解继承是理解面向对象程序设计的关键。4在C++中,给我们提供了一种重要的机制,就是继承。理解继承#include<iostream.h>classAnimal{public:voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classfish{public:voideat();{cout<<"fisheat"<<endl;}voidsleep();{cout<<"fishsleep"<<endl;}voidbreathe(){cout<<"fishbreathe"<<endl;}};类的继承再定义一个绵羊类?5#include<iostream.h>类的继承再定义一个#include<iostream.h>classAnimal{public:voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{};voidmain(){ Animalan;Fishfh; an.eat(); fh.eat();类的继承基类(父类)派生类(子类)派生类除了自己的成员变量和成员方法外,还可以继承基类的成员变量和成员方法。6#include<iostream.h>类的继承基类(父类1继承与派生
1.1继承与派生的概念
1.定义:继承:保持已有类的特性而构造新类的过程。派生:在已有类的基础上新增自己的特性而产生新类的过程。
2.可行性:对象具有自然相关性。
3.基类和派生类:被继承特性的类称为基类(或父类);新增特性从而派生出的类称为派生类(或子类)。71继承与派生1.1继承与派生的概念7继承与派生的目的继承的目的:实现代码重用。派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。8继承与派生的目的继承的目的:实现代码重用。8
交通工具分类层次图
根据事物的实际特征,抓住其共同特性和细小差别,利用分类的方法进行分析和描述反映了交通工具的派生关系由上到下,是一个具体化、特殊化的过程由下到上,是一个抽象化的过程上下层之间的关系就可以看作是基类与派生类的关系。9交通工具分类层次图根据事物的实际特征,抓住其共同特性和
1.2派生类的声明
1.语法:class<派生类名>:[继承方式]<基类名>{派生类成员声明;
};继承方式:private、public和protected,规定了如何访问从基类继承的成员。系统的默认值为私有继承(private)。派生类成员:从基类继承来的所有成员新增加的数据和函数成员——派生类不同于基类的关键所在.2.如何设计派生类成员:比较基类和派生类之间的相同和差异,相同点则加以继承(不必再定义出);而对差异部分则加以扩充(新增、重载overload或者覆盖override
),必须定义出。101.2派生类的声明10
例,从基类vehicle(汽车)公有派生car(小汽车)类的声明形式如下:classvehicle {private: intwheels; floatweight;public: voidinitvehicle(intin_wheels,floatin_weight); intget_wheels(); floatget_weight(); //...};11例,从基类vehicle(汽车)公有派生car(小汽车)类classcar:publicvehicle {private: //新增私有数据成员
intpassenger_load;public: //新增公有函数成员voidinitcar(intin_wheels,floatin_weight,intpeople=4);intget_passengers(); //...};12classcar:publicvehicle 12
1.3派生类生成过程派生新类过程三个步骤:吸收基类成员—代码重用的过程改造基类成员添加新的成员1.吸收基类成员派生类包含了它的所有基类中除构造和析构函数之外的所有成员。2.改造基类成员(1)对基类成员的访问控制---主要依靠派生类声明时的继承方式来控制。
原有代码的扩充过程131.3派生类生成过程原有代码的扩充过程13(2)对基类数据或函数成员的覆盖---在派生类中声明一个和基类数据或函数同名的成员,参数表也要相同,(参数不同的情况属于重载),派生的新成员就覆盖了外层同名成员。3.添加新的成员---是继承与派生机制的核心,是保证派生类在功能上有所发展的关键。
14(2)对基类数据或函数成员的覆盖---在派生类中声明一个和基classemployee{protected: char*name; //姓名
intindividualEmpNo; //个人编号
intgrade; //级别
floataccumPay; //月薪总额
staticintemployeeNo;//本公司职员编号目前最大值
public: employee(); ~employee(); voidpay(); //计算月薪函数
voidpromote(int); //升级函数
voiddisplayStatus(); //显示人员信息
};15classemployee15classtechnician:publicemployee{private: floathourlyRate; //每小时酬金
intworkHours; //当月工作时数public: technician(); //构造函数
voidpay(); //计算月薪函数
voiddisplayStatus(); //显示人员信息};继承了基类employee中成员:name,individualEmpNo,grade,accumPay,employeeNo,pay(),promote(int),displayStatus()。pay()和displayStatus()函数覆盖了基类employee中的同名函数。新添加的数据成员16classtechnician:publicemploy2类的继承方式2.1类的继承方式与类中成员的访问属性的不同:1.类中成员的访问属性:(1)public:不仅能被类中其它成员访问,也能在类外被类的对象访问。(2)private:只能被类中其它成员访问,不能在类外被类的对象访问。(3)protected:类同于private,其差别表现在继承与派生时对派生类的影响不同。172类的继承方式2.1类的继承方式与类中成员的访问属性的不
2.类的继承方式:
(1)public(公有)继承(2)protected(保护)继承(3)private(私有)继承对于不同的继承方式,会导致基类成员原来的访问属性在派生类中有所变化。182.类的继承方式:18类的继承访问特性基类的访问特性类的继承特性子类的访问特性PublicProtectedPrivatePublicPublicProtectedNoaccessPublicProtectedPrivateProtectedProtectedProtectedNoaccessPublicProtectedPrivatePrivatePrivatePrivateNoaccess19类的继承访问特性基类的访问特性类的继承特性子类的访问特性P2.4应用要点:public和private继承方式的区别:(1)public公有继承方式揭示了父类与子类在概念上的相关性;子类应该是父类的特化,当描述具有泛化和特化概念的俩个实体时,应采用public继承方式。(2)private私有继承方式则主要着重于实现代码重用,其主要目的是为了继承父类的接口或父类的数据结构、某些成员函数等。在概念上不具有泛化与特化的子类型关系。如表单类Table从点Point类继承,程序员的主要目的是要重用基类中的GetX()、GetY()等函数。此时应采用private继承方式,而不宜使用public继承方式。202.4应用要点:20classPoint{public:Point(intx,inty){PointX=x;PointY=y;}intGetX(){returnPointX;}intGetY(){returnPointY;}private:intPointX,PointY;};classCircle:
publicPoint{public:Circle(intX,intY,intR):Point(x,y){Radius=R;}//…其它成员定义private:intRadius;};
21classPoint21classTable:privatePoint{public:Table(intRow,intColumn):Point(Row,Column){}intGetRow(){returnGetX();}intGetColumn{returnGetY();}//…其它成员定义};22classTable:privatePtected保护型继承是前两种的折中。对建立其所在类对象的模块来说,它与private成员的性质相同。对于其派生类来说,它与public成员的性质相同。既实现了数据隐藏,又方便继承,实现代码重用。232.protected保护型继承是前两种的折中。23基类模块派生类模块public,private,protected模块H模块V水平访问垂直访问24基类模块派生类模块public,private,protec例protected成员举例classA{protected:intx;}intmain(){Aa;a.x=5;//错误}25例protected成员举例classA{25classA{protected:intx;}classB:publicA{public:voidFunction();};voidB::Function(){x=5;//正确}2626classA{26262727
继承与派生访问属性总结①不可访问的成员:来源:基类私有成员访问属性:派生类、派生类对象都无法访问,继续派生,也无法访问。②私有成员:来源:从基类继承的成员(私有继承)及新增的私有成员访问属性:派生类内部可以访问,派生类对象无法访问,继续派生,无法访问。③保护成员:来源:从基类继承的成员(保护继承)及新增的保护成员访问属性:派生类内部可以访问,派生类对象无法访问,继续派生,在新的派生类中可能成为私有成员(私有继承)或保护成员(公有继承和保护继承时)。④公有成员:来源:从基类继承的成员(公有继承)及新增的公有成员访问属性:派生类、派生类对象都可以访问,继续派生,在新的派生类中可能成为私有成员(私有继承)或保护成员(保护继承时)或公有成员(公有成员继承时)28继承与派生访问属性总结283多继承
C++允许派生类以两种形式继承基类,即单一(简单)继承和多重继承1.含义:单一继承---派生类只从一个基类派生。多重继承---派生类从多个基类派生。2.派生类的定义语法:
class<派生类名>:[继承方式]基类名1,[继承方式]基类名2,...,[继承方式]基类名n{
派生类成员声明;
};3.在多继承时,C++并没有限制基类的个数,但不能有相同的基类,classZ:publicX,publicY,publicX//非法:X出现两次
{//...
};B1{b1}B2{b2}C{c1}293多继承C++允许派生类以两种形式继承基类,即单例:
classBase1{ //...};classBase2{ //...};
classMultiDerived:publicBase1,privateBase2{public: MultiDerived(); ~MultiDerived(); //...};30例:30
3.类族类族:一个相互关联的类的家族。直接基类:在类族中,直接参与派生出某类的基类;间接基类:基类的基类甚至更高层的基类。
313.类族31#include<iostream.h>classA{public:voidf1();protected:intj1;private:inti1;};classB:publicA{public:voidf2();protected:intj2;private:inti2;};classC:publicB{public:voidf3();};回答下列问题:1.派生类B中成员函数f2()能否访问基类A中的成员:f1()、i1和j1呢?2.派生类B中对象b1能否访问基类A中的成员:f1()、i1和j1呢?3.派生类C中成员函数f3()能否访问直接基类B中的成员:f2()和j2呢?能否访问间接基类A中的成员:f1()、i1和j1呢?4.派生类C中对象c1能否访问直接基类B中的成员:f2()、i2和j2呢?能否访问间接基类A中的成员:f1()、i1和j1呢?32#include<iostream.h>classC:pu#include<iostream.h>classA{public:voidf1();protected:intj1;private:inti1;};classB:privateA{public:voidf2();protected:intj2;private:inti2;};classC:privateB{public:voidf3();};回答下列问题:1.派生类B中成员函数f2()能否访问基类A中的成员:f1()、i1和j1呢?2.派生类B中对象b1能否访问基类A中的成员:f1()、i1和j1呢?3.派生类C中成员函数f3()能否访问直接基类B中的成员:f2()和j2呢?能否访问间接基类A中的成员:f1()、i1和j1呢?4.派生类C中对象c1能否访问直接基类B中的成员:f2()、i2和j2呢?能否访问间接基类A中的成员:f1()、i1和j1呢?33#include<iostream.h>classC:pclassC:protectedB{public:voidf3();};回答下列问题:1.派生类B中成员函数f2()能否访问基类A中的成员:f1()、i1和j1呢?2.派生类B中对象b1能否访问基类A中的成员:f1()、i1和j1呢?3.派生类C中成员函数f3()能否访问直接基类B中的成员:f2()和j2呢?能否访问间接基类A中的成员:f1()、i1和j1呢?4.派生类C中对象c1能否访问直接基类B中的成员:f2()、i2和j2呢?能否访问间接基类A中的成员:f1()、i1和j1呢?#include<iostream.h>classA{public:voidf1();protected:intj1;private:inti1;};classB:protectedA{public:voidf2();protected:intj2;private:inti2;};34classC:protectedB#include<i4派生类的构造和析构函数派生类没有继承基类的构造函数,必须自行定义出。1.规则:构造函数必须包含对直接基类的构造函数的调用。2.形式:(1)单一继承时<派生类名>::<派生类名>(基类形参定义,派生类自己的形参定义):基类的构造函数名(形参名){
派生类成员的初始化语句;}354派生类的构造和析构函数派生类没有继
(2)多重继承时:
<派生类名>::<派生类名>(基类1的形参定义,…基类n的形参定义,派生类自己的形参定义):基类名1(形参名1),...,基类名n(形参名n){
派生类新增成员的初始化语句;
}36(2)多重继承时:36
(2)多重继承并且带有对象数据成员时:
<派生类名>::<派生类名>(基类1的形参定义,…基类n的形参定义,对象数据成员所需参数定义,派生类自己的形参定义):基类名1(参数表1),...,基类名n(参数表n),内嵌对象名1(内嵌对象参数表1),...,内嵌对象名m(内嵌对象参数表m){
派生类新增成员的初始化语句;
}37(2)多重继承并且带有对象数据成员时:37
注意:(1)对于使用默认构造函数的基类,可以在派生类的构造函数中不给出基类构造函数名。(2)对于对象成员,如果是使用默认构造函数,也不需要写出对象名和参数表。(3)派生类构造函数的执行顺序一般是,先祖先(基类),再客人(内嵌对象),后自己(派生类本身)。
(4)派生类没有定义构造函数,必须保证基类有缺省参数的构造函数。38注意:38
#include<iostream.h>classvehicle {private:
intwheels;floatweight;public: vehicle(intin_wheels,floatin_weight) {wheels=in_wheels;weight=in_weight;} //...};39#include<iostream.h>39classcar:publicvehicle {private: //新增私有数据成员
intpassenger_load;public: //派生类car的构造函数
car(intin_wheels,floatin_weight,intpeople=4):vehicle(in_wheels,in_weight) {passenger_load=people;} //...};voidmain(){ carbluebird(4,3); //声明派生类car的对象//...}40classcar:publicvehicle 40#include<iostream.h>classAnimal{public:Animal(){cout<<"animalconstruct"<<endl;}
voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish(){cout<<"Fishconstruct"<<endl;}
};voidmain(){ Fishfh;}
animalconstructFishconstruct没有父亲就没有孩子缺省参数的构造函数。41#include<iostream.h>animalco#include<iostream.h>classAnimal{public://Animal()//{cout<<"animalconstruct"<<endl;}
voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish(){cout<<"Fishconstruct"<<endl;}
};voidmain(){ Fishfh;}
Fishconstruct缺省参数的构造函数。42#include<iostream.h>Fishcons#include<iostream.h>classAnimal{public://Animal()//{cout<<"animalconstruct"<<endl;}
voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public://Fish()//{cout<<"Fishconstruct"<<endl;}
};voidmain(){ Fishfh;}
warningC4101:'fh':unreferencedlocalvariable缺省参数的构造函数。43#include<iostream.h>warningC#include<iostream.h>classAnimal{public:Animal(){cout<<"animalconstruct"<<endl;}
voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public://Fish()//{cout<<"Fishconstruct"<<endl;}
};voidmain(){ Fishfh;}
animalconstruct缺省参数的构造函数。44#include<iostream.h>animalco在子类中调用父类的带参数的构造函数#include<iostream.h>classAnimal{public:Animal(intheight,intweight){cout<<"animalconstruct"<<endl;}
~Animal(){cout<<"animaldestruct"<<endl;}voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish(){cout<<"Fishconstruct"<<endl;}
~Fish(){cout<<"Fishdestruct"<<endl;}
};voidmain(){ Fishfh;} noappropriatedefaultconstructoravailable45在子类中调用父类的带参数的构造函数#include<ios在子类中调用父类的带参数的构造函数#include<iostream.h>classAnimal{public:Animal(intheight,intweight){cout<<"animalconstruct"<<endl;}
~Animal(){cout<<"animaldestruct"<<endl;}voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish():Animal(300,400),a(1){cout<<"Fishconstruct"<<endl;}
~Fish(){cout<<"Fishdestruct"<<endl;}private:
constinta;};voidmain(){ Fishfh;} 显式地去调用父类的带参数的构造函数,类中的常量成员也采取此法初始化。46在子类中调用父类的带参数的构造函数#include<ios拷贝构造函数若建立派生类对象时调用缺省拷贝构造函数,则编译器将自动调用基类的缺省拷贝构造函数。若编写派生类的拷贝构造函数,则需要为基类相应的拷贝构造函数传递参数。例如:C::C(C&c1):B(c1){…}47拷贝构造函数若建立派生类对象时调用缺省拷贝构造函数,则编译器
3.
析构函数---派生类没有继承基类的析构函数,必须自行定义出。
(1)规则:只需要为本层次的类定义出析构函数,不需显示调用(由系统隐式调用)直接基类的析构函数.(2)定义:类同于一般的析构函数的定义.(3)原因:因为一个类中只有一个析构函数(无重载形式),能由系统准确识别并且自动隐式调用,系统会自己调用基类及成员对象的析构函数来对基类及对象成员进行清理。它的执行顺序和构造函数正好严格相反——先自己(派生类本身),再客人(内嵌对象),后祖先(基类)。483.析构函数---派生类没有继承基类的析构函数,必须4.应用要点:(1)构造函数的调用次序:先祖先(基类),再客人(内嵌对象),后自己(派生类本身)。而析构函数则相反,先自己(派生类本身),再客人(内嵌对象),后祖先(基类)。(2)派生类只需为它的直接基类定义构造函数的形参并调用它,而不需为它的上上基类(爷爷类)定义形参(区别虚基类构造函数的形参定义);(3)一旦基类中定义出构造函数和析构函数,派生类也必须定义出它们,否则全部采用缺省形式.(4)基类及内嵌对象构造函数的调用顺序:
基类:按照派生类声明时的顺序.
内嵌对象:按照成员在类中声明的顺序,494.应用要点:49继承的析构函数#include<iostream.h>classAnimal{public:
Animal(){cout<<"animalconstruct"<<endl;}
~Animal(){cout<<"animaldestruct"<<endl;}
voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish(){cout<<"Fishconstruct"<<endl;}
~Fish(){cout<<"Fishdestruct"<<endl;}
};voidmain(){ Fishfh;}
animalconstructFishconstructFishdestructanimaldestruct50继承的析构函数#include<iostream.h>an【例】派生类的构造函数和析构函数(多继承,含有内嵌对象)。#include<iostream.h>classBase1 //基类Base1,构造函数有参数{public: Base1(inti){cout<<"constructingBase1"<<i<<endl;} ~Base1(){cout<<"destructingBase1"<<endl;} };classBase2 //基类Base2,构造函数有参数{public: Base2(intj){cout<<"constructingBase2"<<j<<endl;} ~Base2(){cout<<"destructingBase2"<<endl;} };51【例】派生类的构造函数和析构函数(多继承,含有内嵌对象)。5classBase3 //基类Base3,构造函数无参数{public: Base3(){cout<<"constructingBase3"<<endl;} ~Base3(){cout<<"destructingBase3"<<endl;} };52classBase3 //基类Base3,构造函数无参数classDerive:publicBase2,publicBase1,publicBase3{private: Base1memberBase1; Base2memberBase2; Base3memberBase3;public: Derive(inta,intb,intc,intd):Base2(b),memberBase2(d),memberBase1(c),Base1(a){}};voidmain(){ Deriveobject(2,4,6,8);}constructingBase24constructingBase12constructingBase3constructingBase16constructingBase28constructingBase3destructingBase3destructingBase2destructingBase1destructingBase3destructingBase1destructingBase2
Base1Base2Base3Derive53classDerive:publicBase2,publ5派生类中成员的标识与访问5.1访问时的二义性:在多重继承时,基类与派生类之间或基类之间出现同名时,将出现访问时的二义性(不确定性)。产生场合1:基类与派生类之间或基类之间出现同名成员---采用虚函数或同名覆盖来解决。同名覆盖原则:派生类覆盖基类中同名成员;未强行指明则为派生类同名成员;如访问被同名覆盖的同名基类成员,应使用基类名限定。产生场合2:派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问共同基类中的成员时,将产生二义性---采用虚基类来解决。545派生类中成员的标识与访问5.1访问时的二义性:在多重继5.2作用域分辨符“::”1.作用:用来限定要访问的成员归属哪个类:
<类名>::<成员名><类名>::<成员名>(参数表)
在类的派生层次结构中,基类的成员和派生类新增的成员都具有类作用域。二者的作用范围不同,是相互包含的两个层,派生类在内层。555.2作用域分辨符“::”55
【例】继承中使用作用域分辨符。#include<iostream.h>classBase {public: intn;voidfun(){cout<<"ThisisBase,n="<<n<<endl;}};classDerive:publicBase {public: intn; //同名数据成员
voidfun(){cout<<"ThisisDerive,n="<<n<<endl;} //同名函数成员};56【例】继承中使用作用域分辨符。56voidmain(){ Deriveobj; obj.n=1; //对象名.成员名标识
obj.fun(); //对象名.成员函数名标识
obj.Base::n=2; //作用域分辨符标识
obj.Base::fun(); //访问Base基类成员}程序运行结果为ThisisDerive,n=1ThisisBase,n=257voidmain()程序运行结果为ThisisDeri
2.多继承中作用域分辨符的使用
B{b}B1{b1}B2{b2}C从上一级基类继承来的成员拥有相同的名称。对这种类型的同名成员也要使用作用域分辨符来唯一标识,而且必须用直接基类来进行限定。bb1bb2cBBB1B2C582.多继承中作用域分辨符的使用B{b}B1{b1}B2{b【例】多继承中使用作用域分辨符。59【例】多继承中使用作用域分辨符。59#include<iostream.h>classLevel1 {public: intn1; voidfun1(){cout<<"ThisisLevel1,n1="<<n1<<endl;}};classLevel21:publicLevel1 {public: intn21;};60#include<iostream.h>60classLevel22:publicLevel1 {public: intn22;};classLevel3:publicLevel21,publicLevel22{public: intn3; voidfun3(){cout<<"ThisisLevel3,n3="<<n3<<endl;}};61classLevel22:publicLevel1 61voidmain(){ Level3obj; obj.n3=1; obj.fun3(); obj.Level21::n1=2; //使用直接基类
obj.Level21::fun1(); //使用直接基类
obj.Level22::n1=3; //使用直接基类
obj.Level22::fun1(); //使用直接基类}程序运行结果为
ThisisLevel3,n3=1ThisisLevel1,n1=2ThisisLevel1,n1=362voidmain()程序运行结果为626虚基类
6.1虚基类:在继承时以virtual加以修饰的基类。
1.定义语法:各个虚基类的说明位置无先后次序要求,每个virtual只对其后的基类名起作用。
class<派生类>:virtual[继承方式]<基类名>2.作用:它主要用来解决多重继承时可能发生的对同一基类继承多次而产生二义性的问题,为最远的派生类(规定将在建立对象时所指定的类称为最远的派生类)提供一份基类的成员而不重复产生多次拷贝。
3.如何判断是否为虚基类的问题:从某一个起点出发,经过不同的途径,最后又汇合在一起;此共同的起点(基类)应为虚基类。
636虚基类6.1虚基类:在继承时以virtual加以修【例】虚基类。派生时声明Level1为虚基类通过Level21和Level22两条派生路径继承来的基类Level1中的成员n1和fun1()只有一份拷贝。
64【例】虚基类。派生时声明Level1为虚基类通过Level2
b1b2bBB1B2Cbb1bb2cBBB1B2CB{b}B1{b1}B2{b2}C使用虚基类后65b1b2bBB1B2Cbb1bb2cBBB1B2CB{b}
#include<iostream.h>classLevel1{public: intn1; voidfun1(){cout<<"ThisisLevel1,n1="<<n1<<endl;}};classLevel21:virtualpublicLevel1 {public: intn21;};66#include<iostream.h>66classLevel22:virtualpublicLevel1 {public: intn22;};classLevel3:publicLevel21,publicLevel22 {public: intn3; voidfun3(){cout<<"ThisisLevel3,n3="<<n3<<endl;}};67classLevel22:virtualpublicL
voidmain(){ Level3obj; obj.n3=1; obj.fun3(); obj.n1=2; //采用虚基类后,直接使用
obj.fun1(); //“对象名.成员名”方式}68voidmain()68
6.2虚基类及其派生类的构造函数
1.特点:虚基类和一般基类的最大差别在于派生类的构造函数定义。
2.规则:最远的派生类的构造函数不仅需要分别对它的直接基类初始化,也需要对共同基类(虚基类)初始化并且调用其构造函数;
3.C++规定,虚基类子对象是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。而派生类的基类中所列出对这个虚基类的构造函数调用在执行中被忽略,这样便保证了对虚基类的子对象只初始化一次。696.2虚基类及其派生类的构造函数694.虚基类的初始化与一般的多继承的初始化在语法上是一样的,但构造函数的调用次序不同。①虚基类的构造函数在非虚基类之前调用。②若同一层次中包含多个虚基类,这些虚基类的构造函数按它们说明的次序调用。③若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数。
704.虚基类的初始化与一般的多继承的初始化在语法上是一样的,但【例】虚基类及其派生类的构造函数。#include<iostream.h>classLevel1 {public: intn1; Level1(intin_n1){n1=in_n1;cout<<"ThisisLevel1,n1="<<n1<<endl;}};classLevel21:virtualpublicLevel1 {public: intn21; Level21(inta):Level1(a){n21=a;cout<<"ThisisLevel21,n21="<<n21<<endl;}};71【例】虚基类及其派生类的构造函数。71
classLevel22:virtualpublicLevel1 {public: intn22; Level22(inta):Level1(a){n22=a;cout<<"ThisisLevel22,n22="<<n22<<endl;}};classLevel3:publicLevel21,publicLevel22{public: intn3; Level3(inta):Level1(a),Level21(a),Level22(a){n3=a;cout<<"ThisisLevel3,n3="<<n3<<endl;}};voidmain(){ Level3obj(3); } 程序运行结果为ThisisLevel1,n1=3ThisisLevel21,n21=3ThisisLevel22,n22=3ThisisLevel3,n3=372classLevel22:virtualpublic不管是私有派生还是公有派生,派生类都无权访问基类的私有成员。派生类想要使用基类的私有成员,只能通过调用基类的成员函数来实现,也就是使用基类所提供的接口。对于需要频繁访问基类私有成员的派生类-------寻求直接访问基类私有成员的方式。有两种方式。1.在类定义体中增加保护段2.将需访问基类私有成员的派生类成员函数声明为基类的友元7基类私有成员的访问73不管是私有派生还是公有派生,派生类都无权访问基类的私有成
1.在类定义体中增加保护段为了便于派生类的访问,可以将基类的私有成员中需提供给派生类访问的部分定义为保护段成员。保护段成员可以被它的派生类访问,但是对于外界是隐藏的。这样,即方便了派生类的访问,又禁止了外界对它的操作。
741.在类定义体中增加保护段74#include<iostream.h>classvehicle {protected: //保护数据成员
intwheels;floatweight;public: //公有函数成员
vehicle(intin_wheels,floatin_weight) {wheels=in_wheels;weight=in_weight;}intget_wheels(){returnwheels;}floatget_weight(){returnweight;}floatwheel_load(){returnweight/wheels;}voidprint();};【例】在类定义体中增加保护段直接访问基类私有成员。75#include<iostream.h>【例】在类定义体中增classcar:vehicle {private: //新增私有数据成员
intpassenger_load;public: //新增公有函数成员
car(intin_wheels,floatin_weight,intpeople=4):vehicle(in_wheels,in_weight) {passenger_load=people;}intget_passengers(){returnpassenger_load;}voidprint();};vehicle{wheels,weight}car{passenger_load}truck{passenger_load,payload}76classcar:vehicle vehicle{wheeclasstruck:vehicle {private: //新增私有数据成员
intpassenger_load;floatpayload;public: //新增公有函数成员
truck(intin_wheels,floatin_weight,intpeople=2,floatmax_load=24000.00):vehicle(in_wheels,in_weight) {passenger_load=people;payload=max_load;}intget_passengers(){returnpassenger_load;}floatefficiency(){returnpayload/(payload+weight);}voidprint();};77classtruck:vehicle 77voidvehicle::print() //输出汽车类vehicle的数据{cout<<"thewheelsofvehicleis"<<wheels<<endl;cout<<"theweightofvehicleis"<<weight<<endl;cout<<endl;}voidcar::print() {cout<<"thewheelsofcaris"<<wheels<<endl;cout<<"theweightofcaris"<<weight<<endl;cout<<"thepassenger_loadofcaris"<<passenger_load<<endl;cout<<endl;}78voidvehicle::print() //输出汽车类vvoidtruck::print() //输出卡车类truck的数据{cout<<"thewheelsoftruckis"<<wheels<<endl;cout<<"theweightoftruckis"<<weight<<endl;cout<<"thepassenger_loadoftruckis"<<passenger_load<<endl;cout<<"theefficencyoftruckis"<<efficiency()<<endl;cout<<endl;}79voidtruck::print() //输出卡车类trvoidmain(){carbluebird(4,1000,5); truckdongfeng(10,5000,3,34000); bluebird.print();dongfeng.print();}程序运行结果为Thewheelsofcaris4Theweightofcaris1000Thepassenger_loadofcaris5Thewheelsoftruckis10Theweightoftruckis5000Thepassenger_loadoftruckis3Theefficiencyoftruckis0.87179580voidmain()程序运行结果为801.定义:任何被说明为指向基类对象的指针都可以指向它的公有派生类。【例】引入派生类后的对象指针。#include<iostream.h>#include<string.h>classstring{char*name;intlength;public:string(char*str){8引入派生类后的对象指针811.定义:任何被说明为指向基类对象的指针都可以指向它的公有派length=strlen(str);name=newchar[length+1];strcpy(name,str);}voidshow(){cout<<name<<endl;}};classde_string:publicstring{intage;public:de_string(char*str,intage):string(str){de_string::age=age;}voidshow(){string::show(); cout<<"theageis:"<<age<<endl;}};82length=strlen(str);82main(){strings1("Smith"),*ptr1;//定义string类对象s1及指针ptr1de_strings2("Jean",20),*ptr2;//定义de_string类对象s2及指针ptr2ptr1=&s1; //将ptr1指向s1对象ptr1->show(); //调用string类的成员函数ptr1=&s2;//将ptr1指向string类的派生类de_string的对象s2ptr1->show(); //调用s2对象所属的基类的成员函数show()ptr2=&s2; //将ptr2指向de_string类对象s2ptr2->show(); //调用de_string类的成员函数show()return1;}SmithJeanJeanTheageis:20虽然ptr1指针已经指向了s2对象(ptr1=&s2),但是它所调用的函数(ptr1->show())仍然是其基类对象的成员函数。83main()Smith虽然ptr1指针已经指向了s2对象(p2.应用要点:①可以用一个声明指向基类对象的指针指向它的公有派生的对象,若试图指向它的私有派生的对象则是被禁止的。例如:
classBase //定义基类Base{ //...};classDerive:Base//定义基类Base的私有派生类Derive{//...};842.应用要点:84main(){Baseobj1,*ptr1;Deriveobj2;ptr1=&obj1;ptr1=&obj2; //错误!试图将Base类指针ptr1指向它的私有派生类对象//...return1;}85main()85
②不能将一个声明为指向派生类对象的指针指向其基类的对象。例如:
classBase //定义基类Base{ //...};classDerive:publicBase //定义基类Base的公有派生类Derive{ //...};86②不能将一个声明为指向派生类对象的指针指向其基类的对象。例main(){ Baseobj1; Deriveobj2,*ptr; ptr=&obj2; ptr=&obj1; //错误!试图将派生类指针ptr指向基类对象
//... return1;}8787③声明为指向基类对象的指针,当其指向派生类对象时,只能利用它来直接访问派生类中从基类继承来的成员,不能直接访问公有派生类中特定的成员。
classBase {public:voidshow1();};classDerive:publicBase{voidshow2();};88③声明为指向基类对象的指针,当其指向派生类对象时,只能利用它main(){Baseobj1,*ptr;Deriveobj2;ptr=&obj1;ptr->show1();ptr=&obj2;ptr->show1();ptr->show2();//错误!试图调用派生类的特定成员
//...return1;}若想访问其公有派生类的特定成员,可以将基类指针显式类型转换为派生类指针来实现。89main()若想访问其公有派生类的特定成员,可以将基类指针显赋值兼容规则:在公有继承前提下,除了派生类对象的地址可以赋给指向基类的指针,派生类的对象也可以赋值给基类对象,还可用派生类的对象初始化基类的引用。也就是说,在需要基类对象的任何地方都可以使用公有派生类的对象来替代。在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。90赋值兼容规则:在公有继承前提下,除了派生类对象的地址
classBase{//…};classDerive:publicBase{//…};voidmain(){BaseobjBase,*pb; DeriveobjDerive;}9191①派生类对象可以赋值给基类对象,即用派生类对象中从基类继承来的成员赋值给基类对象的成员
objBase=objDerive;②派生类的对象也可以初始化基类对象的引用objBase1:
Base&objBase1=objDerive;③派生类对象的地址也可以赋给指向基类的指针:
pb=&objDerive;
在基类出现的场合使用派生类进行替代,但是替代之后派生类仅仅发挥出基类的作用。92①派生类对象可以赋值给基类对象,即用派生类对象中从基类继承
#include<iostream.h>classA{public: A(constchar*s){cout<<s<<endl;} ~A(){}};classB:virtualpublicA{public: B(constchar*s1,constchar*s2):A(s1) {cout<<s2<<endl; }};classC:virtualpublicA{public: C(constchar*s1,constchar*s2):A(s1) {cout<<s2<<endl; }};classD:publicB,publicC{public: D(constchar*s1,constchar*s2,constchar*s3,constchar*s4):B(s1,s2),C(s1,s3),A(s1) {cout<<s4<<endl; }};voidmain(){D*ptr=newD("classA","classB","classC","classD");deleteptr;}93#include<iostream.h>classC:vclassAclassBclassCclassDclassAclassBclassAclassCclassD94classAclassA94C++ProgrammingLanguageDr.ZhengXiaojuanAssociateProfessorSoftwareCollegeofNortheastNormalUniversityOctober.200895C++ProgrammingLanguage1
第七章继承与派生
96
第七章继承与派生2
本章内容
1继承与派生2类的继承方式3多继承4派生类的构造和析构函数5派生中成员的标识与访问虚基类基类私有成员的访问引入派生类后的对象指针97本章内容1继承与派生3在C++中,给我们提供了一种重要的机制,就是继承。理解继承是理解面向对象程序设计的关键。98在C++中,给我们提供了一种重要的机制,就是继承。理解继承#include<iostream.h>classAnimal{public:voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classfish{public:voideat();{cout<<"fisheat"<<endl;}voidsleep();{cout<<"fishsleep"<<endl;}voidbreathe(){cout<<"fishbreathe"<<endl;}};类的继承再定义一个绵羊类?99#include<iostream.h>类的继承再定义一个#include<iostream.h>classAnimal{public:voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{};voidma
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 电工工考试题及答案
- 文化产业管理证书考生心得体会
- 精密医学发展的趋势与挑战试题及答案
- 育婴师文化敏感性的提升考核试题及答案
- 林肯事故测试题及答案
- 卫生管理创新方法与案例试题及答案
- 激光多功能应用试题及答案
- 自主招生网络试题及答案
- 污水管道疏通试题及答案
- 西医临床数据收集技巧试题及答案
- 跨境电商平台下的中国二手车出口模式
- 2024国家电投集团中国电力招聘(22人)笔试参考题库附带答案详解
- 2024年辅导员岗位素质试题及答案
- 运动素质知到课后答案智慧树章节测试答案2025年春浙江大学
- 树立正确的婚恋观讲座课件
- 急性阑尾炎中医护理查房
- (高清版)DB12∕T 934-2020 公路工程资料管理技术规程
- 【罗兰贝格】2025全球医疗器械报告-创新与效率平衡之道
- 居间费用分配协议
- 比亚迪入职考试题及答案
- 2025年杭州万向职业技术学院单招职业适应性测试题库及答案1套
评论
0/150
提交评论