《派生类与继承》PPT课件.ppt

上传人:小飞机 文档编号:5635410 上传时间:2023-08-04 格式:PPT 页数:70 大小:423KB
返回 下载 相关 举报
《派生类与继承》PPT课件.ppt_第1页
第1页 / 共70页
《派生类与继承》PPT课件.ppt_第2页
第2页 / 共70页
《派生类与继承》PPT课件.ppt_第3页
第3页 / 共70页
《派生类与继承》PPT课件.ppt_第4页
第4页 / 共70页
《派生类与继承》PPT课件.ppt_第5页
第5页 / 共70页
点击查看更多>>
资源描述

《《派生类与继承》PPT课件.ppt》由会员分享,可在线阅读,更多相关《《派生类与继承》PPT课件.ppt(70页珍藏版)》请在三一办公上搜索。

1、第4章 派生类与继承,4.1 继承与派生类,4.1.1 为什么要使用继承重用:通过继承机制,利用已有的类来定义新类,新的类不仅可以有新的成员,还拥有从已有的类继承来的成员。已有的类称为基类或父类,新的类称为派生类或子类。如果派生类从一个基类派生的,称这种继承为单继承;如果派生类从多个基类派生的,称这种继承为多继承。,4.1 继承与派生类,单继承,4.1 继承与派生类,多继承,4.1 继承与派生类,4.1.2 派生类的声明如果不采用派生类方法class person protected:char name10;int age;char sex;public:void print();,class

2、 employee protected:char name10;int age;char sex;char department20;float salary;public:print();,重复书写!,4.1 继承与派生类,改写/定义一个基类 class person protected:char name10;int age;char sex;public:/;/定义一个派生类 class employee:public person protected:char department20;float salary;public:/;,4.1 继承与派生类,声明一个派生类的一般格式为:cl

3、ass 派生类名:继承方式 基类名 派生类新定义成员;其中,继承方式有3种:public(公有派生)、private(私有派生,缺省情况下也如此)、protected(保护派生)。,由类person继承出类employee可以采用下面的三种格式之一:(1)公有继承 class employee:public person/;(2)私有继承 class employee:private person/;(3)保护继承 class employee:protected person/;,4.1 继承与派生类,派生类的变异功能 从已有类派生出新类时,可以在派生类内完成以下几种功能:(1)可以增加新的

4、数据成员;(2)可以增加新的成员函数;(3)可以重新定义基类中已有的成员函数;(4)可以改变现有成员的属性。,4.1 继承与派生类,4.1.3 基类成员在派生类中的访问属性,4.1 继承与派生类,不可见,可见,可见,4.1.4 派生类对基类成员的访问规则 派生类对基类成员的访问形式主要有以下两种:(1)内部访问:由派生类中新增成员对基类继承来的成员的访问。(2)对象访问:在派生类外部,通过派生类的对象对从基类继承来的成员的访问。,4.1 继承与派生类,私有继承的访问规则基类的public成员和protected成员被继承后作为派生类的private成员,派生类的其他成员可以直接访问它们,但是在

5、类外部通过派生类的对象无法访问。基类的private成员在私有派生类中是不可直接访问的,所以无论是派生类成员还是通过派生类的对象,都无法直接访问从基类继承来的private成员,但是可以通过基类提供的public成员函数间接访问。,4.1 继承与派生类,class fathpublic:int a,b;int getx()return X;int gety()return Y;protected:int c,d;private:int X,Y;,说明:,class son:private fath int W,H;pubulic:void large();,等价于,class son/publ

6、ic:/void large();protected:private:/int W,H;int a,b;int getx()return X;int gety()return Y;int c,d;fatherprivate:int X,Y;,son:son1;son1 可以访问large()son1.large 可以访问 除了X、Y以外的任 何成员,4.1 继承与派生类,例4.1一个私有继承的例子#include iostream.h class x int a;public:x()a=12;int get()return a;void print()cout a endl;class y:x

7、/私有继承 int b;/,4.1 继承与派生类,public:void make()b=get()+10;/调用了基类的成员 void print()x:print();cout b endl;void main()x x1;class y y1;y1.make();y1.get();/错误 x1.get();/对 y1.print();/输出 12 22,4.1 继承与派生类,改变继承访问规则若希望基类中某些公有成员在派生类中也是公有的,使派生类的使用者能访问它,则可在派生类的公有段说明这些成员,说明的格式为:基类名:成员名;,例 class x int a;public:int b;x(

8、)a=12;b=11;int get()return a;void print()cout a endl;class y:x/私有继承/,4.1 继承与派生类,public:void make()b=get()+10;/调用了基类的成员 void print()x:print();cout b endl;x:get;/x类中的get函数 x:b;/x类中的数据成员b;void main()class x x1;class y y1;y1.make();cout y1.get()y1.b endl;/正确 x1.get();/对 y1.print();/输出 12 22,4.1 继承与派生类,通

9、过访问声明调整访问域 访问声明仅仅调整名字的访问,不可为它说明任何类型;若名字为成员函数,在访问声明时,也不准说明任何参数。访问声明只能调整基类的保护段和公有段成员在派生类中的访问域。不允许在派生类中降低或提升基类成员的可访问性。基类的保护段成员只在派生类中的保护段中进行访问声明;基类的公有段成员只能在派生类的公有段中进行声明。,class x int a;protected:int b;public:int c;class y:x pulic:x:b;/error protected:x:c;/error;,4.1 继承与派生类,对重载函数的访问声明将调整基类中具有该名的所有函数的访问域。c

10、lass x public:f();f(int a);f(char*p);.class y:x.pulic:x:f;.;,4.1 继承与派生类,若派生类中具有与基类同名的函数,则基类中的此函数不能在派生类中进行访问声明。class x public:f();f(int a);f(char*p);.class y:x public:void f(int s);x:f;/error;,公有继承的访问规则基类的public成员和protected成员被继承到派生类中仍作为派生类的public成员和protected成员,派生类的其他成员可以直接访问它们。但是,类的外部使用者只能通过派生类的对象访问继

11、承来的public成员。基类的private成员在私有派生类中是不可直接访问的,所以无论是派生类成员还是通过派生类的对象,都无法直接访问从基类继承来的private成员,但是可以通过基类提供的public成员函数间接访问它们。,4.1 继承与派生类,class fathpublic:int a,b;int getx()return X;int gety()return Y;protected:int c,d;private:int X,Y;,说明:,class son:public fath int W,H;pubulic:void large();,等价于,son:son1;son1 可以访

12、问a、b、getx()、gety()、large()son1.large 可以访问 除了X、Y以外的任 何成员,4.1 继承与派生类,例4.3 公有继承的访问规则#include iostream.h class base int x1,x2;public:void assign(int p1,int p2)x1=p1;x2=p2;int inc1()x1+;return x1;int inc2()x2+;return x2;void display()cout x1 x2 endl;class derive1:base int x3;public:derive1(int p3)x3=p3;,

13、4.1 继承与派生类,void assign(int p1,int p2)base:assign(p1,p2);int inc1()return base:inc1();int inc2()return base:inc2();int inc3()x3+;return x3;void display()base:display();cout x3 endl;class derive2:public base int x4;public:derive2(int p4)x4=p4;int inc1()int temp=base:inc1();temp=base:inc1();temp=base:i

14、nc1();return base:inc1();/相当于x1=x1+4;,解决私有派生情况的对外可见问题,4.1 继承与派生类,int inc4()x4+;return x4;void display()base:display();cout x4 endl;int main()base p;p.assign(-2,-2);cout base-:n;p.display();/-2-2 derive1 d1(-4);d1.assign(10,10);cout nderive1-:n;d1.display();/x1=10 x2=10 x3=-4 d1.inc1();d1.inc2();d1.i

15、nc3();cout nderive1-2-:n;,4.1 继承与派生类,d1.display();/x1=11 x2=11 x3=-3 derive2 d2(5);d2.assign(-11,-12);/调用基类成员函数 cout nderive2-:n;d2.display();/x1=-11 x2=-12 x4=5 d2.inc1();/对x1 4次加1 x1=-7 d2.inc4();/x4=6 cout nderive2-:n;d2.display();/x1=-7 x2=-12 x4=6 d2.base:inc1();/x1=-6 cout nderive2-:n;d2.displ

16、ay();return 0;,保护继承的访问规则基类的public成员和protected成员被继承到派生类中都作为派生类的protected成员,派生类的其他成员可以直接访问它们,但是类的外部使用者不能通过派生类的对象来访问它们。基类的private成员在私有派生类中是不可直接访问的,所以无论是派生类成员还是通过派生类的对象,都无法直接访问基类的private成员。,4.1 继承与派生类,class fathpublic:int a,b;int getx()return X;int gety()return Y;protected:int c,d;private:int X,Y;,说明:,c

17、lass son:protected fath int W,H;pubulic:void large();,等价于,class son/public:/void large();protected:int a,b;int getx()return X;int gety()return Y;int c,d;private:/int W,H;fatherprivate:int X,Y;,son:son1;son1 可以访问large()son1.large 可以访问 除了X、Y以外的任 何成员,4.1 继承与派生类,例4.4 保护继承的访问规则。#includeclass Basepublic:i

18、nt z;void setx(int i)x=i;int getx()return x;private:int x;protected:int y;class Derived:protected Basepublic:int p;,4.1 继承与派生类,void setall(int a,int b,int c,int d,int e,int f);void show();private:int m;protected:int n;void Derivedsetall(int a,int b,int c,int d,int e,int f)x=a;/非法,在类Derived中,x为不可直接访问

19、成员,可修改为setx(a);y=b;/合法,y在类derived中为protected成员 z=c;/合法,z在类derived中为protected成员 m=d;n=e;p=f;,4.1 继承与派生类,void Derivedshow()/coutx=xendl;/非法,在类Derived中,x为不可直接访问成员 coutx=getx()endl;/合法,getx()在类derived中为protected成员 couty=yendl;/合法,y在类derived中为protected成员 coutz=zendl;/合法,z在类derived中为protected成员 coutm=mend

20、l;coutn=nendl;void main()Derived obj;obj.setall(1,2,3,4,5,6);obj.show();coutp=obj.pendl;/合法,p在类derived中为public成员x=1y=2z=3m=4n=5p=6,4.2.1 派生类构造函数和析构函数的执行顺序当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数;当撤消派生类对象时,则先执行派生类的析构函数,随后再执行基类的析构函数。例4.5 基类和派生类的构造函数及析构函数的执行顺序。#include class Base public:Base()coutConstructi

21、ng base classn;/基类的构造函数,4.2 派生类的构造函数和析构函数,4.2 派生类的构造函数和析构函数,Base()coutDestructing base classn;/基类的析构函数;class Derive:public Base public:Derive()coutConstructing derived classn;/派生类的构造函数 Derive()coutDestructing derived classn;/派生类的析构函数;main()Derive op;return 0;,构建:先人后己!,撤销:先己后人!,输出:Constructing base c

22、lassConstructing derived classDestructing derived classDestructing base class,4.2.2 派生类构造函数和析构函数的构造规则下面两种情况下,必须定义派生类的构造函数:派生类本身需要构造函数在定义派生类对象时,其相应基类对象需要调用带有参数的构造函数派生类构造函数的一般格式为:派生类名(参数总表):基类名(参数表)/派生类新增成员的初始化语句例4.6 当基类含有带参数的构造函数时,派生类构造函数的构造方法。,4.2 派生类的构造函数和析构函数,#include class Base public:Base(int n)

23、/基类的构造函数 coutConstructing base classn;i=n;Base()/基类的析构函数 coutDestructing base classn;void showi()coutiendl;private:int i;class Derive:public Base public:,4.2 派生类的构造函数和析构函数,Derive(int n,int m):Base(m)/定义派生类构造函数时,/缀上基类的构造函数 coutConstructing derived classendl;j=n;Derive()/派生类的析构函数 coutDestructing deriv

24、ed classendl;void showj()coutjendl;private:int j;main()Derive obj(50,60);obj.showi();obj.showj();return 0;,4.2 派生类的构造函数和析构函数,当派生类中含有内嵌对象成员时,其构造函数的一般形式为:派生类名(参数总表):基类名(参数表1),内嵌对象名1(内嵌对象参数表1),内嵌对象名n(内嵌对象参数表n)/派生类新增成员的初始化语句 例4.7 内嵌对象成员时派生类构造函数和析构函数的执行顺序#includeclass Base int x;public:Base(int i)x=i;cou

25、tConstructing base classn;Base()coutDestructing base classn;/基类的析构函数 void show()cout x=xendl;,4.2 派生类的构造函数和析构函数,4.2 派生类的构造函数和析构函数,class Derived:public Base Base d;/d为基类对象,作为派生类的对象成员public:Derived(int i):Base(i),d(i)/派生类的构造函数,缀上/基类构造函数和对象成员构造函数 coutConstructing derived classn;Derived()/派生类的析构函数 coutD

26、estructing derived classn;main()Derived obj(123);obj.show();return 0;,输出:Constructing base classConstructing base classConstructing derived classx=123Destructing derived classDestructing base classDestructing base class,构建:先人后己!,撤销:先己后人!,4.2 派生类的构造函数和析构函数,在定义派生类对象时,构造函数的执行顺序如下:调用基类的构造函数;调用内嵌对象成员的构造函

27、数(有多个对象成员时,调用顺序由它们在类中声明的顺序确定);派生类的构造函数体中的内容;撤消对象时,析构函数的调用顺序与构造函数的调用顺序正好相反。,4.3.1 同名成员,4.3 调整基类成员在派生类中的访问属性的其他方法,当派生类与基类中有相同成员时:若未强行指名,则通过派生类对象使用的是派生类中的同名成员。如要通过派生类对象访问基类中被覆盖的同名成员,应使用基类名限定。例子:同名隐藏,#include class B1/声明基类B1 public:/外部接口int nV;void fun()coutMember of B1endl;class B2/声明基类B2 public:/外部接口i

28、nt nV;void fun()coutMember of B2endl;class D1:public B1,public B2 public:int nV;/同名数据成员void fun()coutMember of D1endl;/同名函数成员;,void main()D1 d1;d1.nV=1;/对象名.成员名标识,访问D1类成员d1.fun();d1.B1:nV=2;/作用域分辨符标识,访问基类B1成员d1.B1:fun();d1.B2:nV=3;/作用域分辨符标识,访问基类B2成员d1.B2:fun();,4.1 继承与派生类,2,访问声明已在部分中私有继承后面讲解。,4.3 调整

29、基类成员在派生类中的访问属性的其他方法,4.4 多继承,派生类只有一个基类,这种派生方法称为单基派生或单继承。当一个派生类具有多个基类时,这种派生方法称为多基派生或多继承。4.4.1 多继承的声明有两个以上基类的派生类声明的一般形式如下:class 派生类名:继承方式1 基类名1,继承方式n 基类名n/派生类新增的数据成员和成员函数;例,4.4 多继承,class a1 int x1;public:int y1;void print1()cout x1 y1 endl;class a2 int x2;public:int y2;void print2()cout x2 y2 endl;clas

30、s a3 int x3;public:int y3;void print3()cout x3 y3 endl;class a4:public a1,public a2,a3 int x4;public:int y4;void print2()cout x4 y4 endl;,4.4 多继承,4.4.2 多继承的构造函数与析构函数 多继承构造函数定义的一般形式如下:派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),基类名n(参数表n)/派生类新增成员的初始化语句 派生类构造函数的执行顺序先是基类构造函数,再执行派生类本身的构造函数。处于同一层次的各基类构造函数执行顺序取决于定义派

31、生类时所指定的各基类顺序,与派生类构造函数中所定义的初始化列表的各项顺序无关。,4.4 多继承,例#include iostream.h class B1 int b1;public:B1(int i)b1=i;cout“Constructor B1.i endl;void print()cout b1 endl;class B2 int b2;public:B2(int i)b2=i;cout Constructor B2.i endl;void print()cout b2 endl;class B3 int b3;public:B3(int i)b3=i;cout Constructor

32、 B3.i endl;int getb3()return b3;class A:public B2,public B1,这里确定了构造顺序为:B2B1A,4.4 多继承,int a;B3 bb;public:A(int i,int j,int k,int m):B1(i),B2(j),bb(k)a=m;cout Constructor A.m endl;void print()B1:print();B2:print();cout a,bb.getb3()endl;void main()A aa(1,2,3,4);aa.print();,初始化顺序不能确定构造顺序。实际顺序为:B2B1B3A,程

33、序的执行结果为:Constructor B2.2 Constructor B1.1 Constructor B3.3 Constructor A.4 1 2 4,3,4.4 多继承,4.4.3 虚基类为什么要引入虚基类解决二义性问题 在多继承情况下,可能出现被访问的基类成员来源有两种以上的可能的不确定性问题,即二义性问题。例#include iostream.h class A int a;public:A(int i=1)a=i;int f()return a;class B int b,c;public:B(int i=1,int j=1)b=i;c=j;,4.4 多继承,int f()r

34、eturn b;int g()return b+c;class C:public A,public B int d,e;public:C(int i=1,int j=1)d=i;e=j;int g()return d;int h()cout f()endl;/error:C:f is ambiguous;/(暧昧的,不明确的)void main()C c1;cout c1.f()endl;/error:C:f is ambiguous cout c1.g()endl;,/可改为:A:f();或 B:f();/可改为:c1.A:f();或 c1.B:f();,4.4 多继承,当在多条继承路径上有

35、一个公共基类,在这些路径的某几条路径的汇合处,这个公共基类会产生多个子对象。若想只保存这个基类的一个实例,可将这个公共基类说明为虚基类。#include iostream.h class A public:int a;class B1:public A int b1;class B2:public A int b2;,4.4 多继承,class C:public B1,public B2 int c;public:int f()return c+A:a;/error:C:a is ambiguous;void main()C c1;c1.a=12;/error:C:a is ambiguous

36、 cout c1.f()endl;,/可改为:c+B1:a 或 c+B2:a/可改为:c1.B1:a 或 c1.B2:a,例4.14 虚基类的引例。#include class base public:base()a=5;coutbase a=aendl;protected:int a;class base1:public base public:base1()a=a+10;coutbase1 a=aendl;class base2:public base public:base2()a=a+20;coutbase2 a=aendl;,4.4 多继承,4.4 多继承,class derived

37、:public base1,public base2 public:derived()coutbase1:a=base1:aendl;coutbase2:a=base2:aendl;main()derived obj;return 0;,程序运行结果如下:base a=5base1 a=15base a=5base2 a=25base1:a=15base2:a=25,base base base1 base2 derived 图5-2 非虚基类的类层次图,4.4 多继承,4.4 多继承,虚基类的概念 虚基类的声明是在派生类的声明过程,其语法形式如下:class 派生类名:virtual 继承方

38、式 类名/,例4.15 虚基类的使用。#include class base public:base()a=5;coutbase a=aendl;protected:int a;class base1:virtual public base public:base1()a=a+10;coutbase1 a=aendl;class base2:virtual public base public:base2()a=a+20;coutbase2 a=aendl;,4.4 多继承,class derived:public base1,public base2 public:derived()cout

39、derived a=aendl;main()derived obj;return 0;程序运行结果如下:base a=5base1 a=15base2 a=35derived a=35,4.4 多继承,base base1 base2 derived 图5-3 虚基类的类层次图,4.4 多继承,虚基类的初始化 在使用虚基类机制时应该注意以下几点:如果在虚基类中定义有带形参的构造函数,并且没有定义缺省形式的构造函数,则整个继承结构中,所有直接或间接的派生类都必须在构造函数的成员初始化表中列出对虚基类构造函数的调用,以初始化在虚基类中定义的数据成员。建立一个对象时,如果这个对象中含有从虚基类继承来

40、的成员,则虚基类的成员是由最终派生类的构造函数通过调用虚基类的构造函数进行初始化的。该派生类的其他基类对虚基类构造函数的调用都自动被忽略。,4.4 多继承,4.4 多继承,例 class A public:A(const char*s)cout s endl;A;class B:virtual pulic A public:B(const char*s1,const char*s2):A(s1)cout s2 endl;,4.4 多继承,class C:virtual public A public:C(const char*s1,const char*s2):A(s1)couts2endl;

41、class D:public B,public Cpublic:D(const char*s1,const char*s2,const char*s3,const char*s4):B(s1,s2),C(s1,s3),A(s1)couts4endl;void main()D*ptr=new D(class A,class B,class C,class D);delete ptr;,4.4 多继承,程序的执行结果为:class A class B class C class D,若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数;对

42、于多个虚基类,构造函数的执行顺序仍然是先左后右,自上而下;对于非虚基类,构造函数的执行顺序仍是先左后右,自上而下;若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数。,4.4 多继承,4.4 多继承,例 class base public:base()cout base class n;class base2 public:base2()cout base class2 n;class level1:public base2,virtual public base public:level1()cout class level1n;,4.4 多继承,class lev

43、el2:public base2,virtual public base public:level2()cout class level2n;class toplevel:public level1,public level2 public:toplevel()cout class topleveln;void main()toplevel t;程序的运行结果为:,class base class base2 class level1 class base2 class level2 class toplevel,所谓赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。这

44、样,公有派生类实际上就具备了基类的所有特性,凡基类能解决的问题,公有派生类也能解决。,4.5 赋值兼容规则,例如,下面声明的两个类:class Base;class Derived:public Base;根据赋值兼容规则,以下几种情况是合法的:,4.5 赋值兼容规则,(1)可以用派生类对象给基类对象赋值。例如:Base b;Derived d;b=d;这样赋值的效果是,对象b中所有数据成员都将具有对象d中对应数据成员的值。(2)可以用派生类对象来初始化基类的引用。例如:Derived d;Base,4.5 赋值兼容规则,(3)可以把派生类对象的地址赋值给指向基类的指针。例如:Derived d;Base,4.5 赋值兼容规则,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号