收藏 分销(赏)

虚基类与虚函数.pptx

上传人:人****来 文档编号:14225431 上传时间:2026-07-21 格式:PPTX 页数:50 大小:374.90KB 下载积分:8 金币
下载 相关
虚基类与虚函数.pptx_第1页
第1页 / 共50页
虚基类与虚函数.pptx_第2页
第2页 / 共50页


点击查看更多>>
资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,2026/7/21 周二,1,5.2.2,虚基类,1.,虚基类概念,在,C+,语言中,一个类不能被屡次说明为一个派生类直接基类,但能够不止一次地成为间接基类。这就造成了一些问题。为了方便 说明,先介绍多继承“类格”表示法。,派生类及其基类可用一有向无环图,(DAG),表示,其中箭头表示“由派生而来”。类,DAG,常称为一个“类格”。复杂类格画出来通常更轻易了解。比如:,第1页,例,5-19,class L,public:,int next;,class A :public L,;,class B :public L,;,class C :public A,public B,public:,void f()next=0;,;,C,类自己数据组员,B,对象数据组员,L,对象数据组员,A,对象数据组员,L,对象数据组员,C,对象,L,A,L,B,C,这时,,next,有两个赋值语句,next=0,;,含有二义性,它是将,A:next,置为零,还是将,B:next,置为零,或者将二者都置为,0,,需要在函数,f(),中被显式说明,.,第2页,2026/7/21 周二,3,假如希望间接基类,L,与其派生类关系是以下列图,C+,语言提供了这种描述伎俩。它将,L,说明为,A,和,B,虚基类。,L,A,B,C,第3页,2026/7/21 周二,4,当在多条继承路径上有一个公共基类,在这些路径中某几条路经汇合处,这个公共基类就会产生多个实例。,假如只想保留这个基类一个实例,能够将这个公共基类说明为虚拟基类或称虚基类。,它仅是简单地将关键字,virtual,加到基类描述上,比如改写上述例子为例,5-20,第4页,例,5-20,class L,public:,int next;,class A :virtual public L,;,class B :virtual public L,;,class C :public A,public B,public:,void f()next=0;,;,这时,C,类对象中只有,L,一个复制,因而函数,C:f(),中语句,next=0;,没有二义性。,对于类,C,而言,,L,类是,B,类虚基类,而是类,A,真基类;但对于类,B,而言,,L,类还是,B,类真基类。,例,5-21,或,class A:public virtual L,或,class A:public virtual,第5页,class L,public:,int next;,;,class A :virtual public L,;,class B :virtual public L,;,class C :public B,public A,public:,void f()next=0;,;,此例中,对于类,C,而言,,L,类是,A,类虚基类,而是类,B,真基类。,派生时,,A,B,次序变了,第6页,2026/7/21 周二,7,一个派生类对象地址能够直接赋给虚基类指针,比如,:,C obj;,L *ptr=,这时不需要强制类型转换,而且,一个虚基类引用能够引用一个派生类对象,比如:,C obj2;,L,反之则不行,不论在强制类型转换中指定什么路径,一个虚基类指针或引用不能转换为派生类指针或引用。比如:,C *P=(C*)(A*)ptr;,将产生编译错误。,第7页,2026/7/21 周二,8,2.,虚基类对象初始化,虚基类初始化与多继承初始化在语法上是一样,但隐含结构函数调用次序有点差异。,虚基类结构函数调用次序是这么要求:,1.,虚基类结构函数在非虚基类之前调用。,2.,若同一层次中包含多个虚基类,虚基类结构函数按它们说明次序调用。,3.,若虚基类由非虚基类派生,则恪守先调用基类结构函数,再调用派生类结构函数规则。,第8页,2026/7/21 周二,9,比如:,class X:public Y,virtual public Z,X one;,将产生以下调用次序:,Z(),Y(),X(),这里,Z,是,X,虚基类,故先调用,Z,结构函数,再调用,Y,结构函数,最终才调用派生类,X,自己结构函数。,例,5-22,第9页,#,include,iostream.h,class,base,public,:base()cout,Base,endl;,class,base2,public,:base2()cout,Base2,endl;,class,level1 :,public,base2,virtual public,base,public,:level1()cout,level1,endl;,class,level2 :,public,base2,virtual public,base,public,:level2()cout,level2,endl;,class,toplevel:,public,level1,virtual public,level2,public,:toplevel()cout,toplevel,endl;,base2,base,base2,level1,level2,toplevel,toplevel view;,void main(),当建立对象,view,时,将产生以下调用次序:,level2(),level1(),toplevel(),而,level2(),要求:,base(),base2(),level2(),level1(),要求,base2(),level1(),toplevel(),要求,toplevel(),所以,结构函数调用次序为:,base(),base2(),level2(),base2(),level1(),toplevel(),第10页,例,5-23,class base,;,class base2,;,class level1 :public base2,virtual public base,;,class level2 :public base2,virtual public base,;,class toplevel:virtual public level1,public level2,;,toplevel view;,base2,base,base2,level1,level2,toplevel,level1():,base(),base2(),level1(),level2():,base2(),level2(),toplevel():,toplevel(),当建立对象,view,时,将产生以下调用次序,:,此例中,对于,toplevel,而言,,base,是,level2,虚基类,第11页,2026/7/21 周二,12,例,5-24,class B,;,class X:virtual public B,;,class Y :virtual public B,;,class Z :public B,;,class AA:public X,public Y,public Z,;,B,B,X,Y,AA,Z,这里,AA,含有两个,B,类子对象:,Z,B,和,x,与,Y,共享虚拟,B,。,第12页,class V,public :,int V;,class A,public :,int a;,class B :public A,virtual public V ;,class C :public A,Virtual public V ;,class D :public B,public C,public:,void f();,;,void D:f(),v+;,a+;,例,5-25,虚基类和非虚基类不一样,。,A,V,A,B,C,D,在,D,中仅仅一个,v,错误,含有二义性,在,D,中有两个,a,调用次序,:,B():,V(),A(),B(),C():,A(),C(),D():,D(),第13页,2026/7/21 周二,14,5.3,虚函数与多态性,对于普通组员函数重载,可表示为下面方式,:,1),在同一个类中重载,2),在不一样类中重载,3),基类组员函数在派生类中重载,所以,重载函数访问是在编译时区分,有以下三种方法,:,第14页,2026/7/21 周二,15,1.,依据参数特征加以区分,比如,:,Show(int,char),与,Show(char*,float),不是同一函数,编译能区分。,2.,使用“,:”,加以区分,比如:,Circle:Show,有别于,Point:Show,3.,依据类对象加以区分。,ACircle.Show(),调用,Circle:Show(),APoint.Show(),调用,Point:Show(),这里,ACircle,和,APoint,分别是,Circle,和,Point,对象。,第15页,例,5-26,#include,class A,public:,void fun()cout“In A”endl;,;,class B:public A,public:,void fun()cout,“,In B,”,endl;,;,class C:public B,public:,void fun()cout,“,In C,”,fun();,派生类对象,B:fun,A:fun,C:fun,第16页,5.3.1,基类对象指针指向派生类对象,指向基类和派生类指针变量是相关,假设,B_class,是基类,,D_class,是从,B_class,公有派生出来派生类,任何被说明为指向,B_class,指针也能够指向,D_class,。比如,:,利用,p,能够访问从基类,B_class,继承组员,但,D_class,自己定义组员,,p,不能访问。比如:例,5-27,指向类型,B_class,对象指针,类型,B_class,对象,类型,D_class,对象,p,指向类型,D_class,对象,它是,B_class,派生类,p,指向类型,B_class,对象,B_class *p;,B_class B_ob;,D_class D_ob;,p=,p=,派生类对象,派生类组员,基类组员,第17页,#,include,#,include,class,B_class,char,name80;,public,:,void put_name(char*s),strcpy(name,s);,void,show_name(),coutnameput_name(Thomas Edison);,p=,p-put_name(Albert Einstein);,B_ob.show_name();,D_ob.show_name();,dp=,dp-put_phone(555555_1234);,dp-show_phone();,p-show_pone();,(D_class*)p)-show_phone();,p-show_phone();,错误,错误,该程序输出,:,Thomas Edison,Albert Einstein,555555_1234,555555_1234,class D_class:public B_class,char phone_num80;,public:,void put_phone(char*num),strcpy(phone_num,num);,void show_phone(),coutphone_numshow_phone();,第19页,5.3.2,虚函数,例,5-28#include,class Base,protected:,int x;,public:,Base(int a),x=a;,void who()cout“base ”x“n”;,;,class Second_d:public Base,public:,Second_d(int a):Base(a),void who(),cout“Second derivation”x“n”;,;,class First_d:public Base,public:,First_d(int a):Base(a),void who(),cout“First derivation”xwho();,p=,p-who();,p=,p-who();,first_obj.who();,second_obj.who();,该程序输出,:,base 1,base 2,base 3,First derivation 2,Second derivation 3,second_obj,x,3,Base:who(),Second_d:who(),first_obj,x,2,Base:who(),first_d:who(),base_obj,x,1,Base:who(),p,p-who(),指向基类指针,p,不论是指向基类对象,base_obj,还是指向派生对象,first_obj,和,second_obj,p-who(),调用都是基类定义,who(),版本,.,必须显式地用,first_obj.who();,和,second_obj.who();,才能调用类,first_d,和类,second_d,中定义,who(),版本。其本质原因在于普通组员函数调用是在编译时静态区分。,第21页,2026/7/21 周二,22,假如伴随,p,所指向对象不一样,pwho(),能调用不一样类中,who(),版本,这么就能够用一个界面,p-who(),访问多个实现版本,:Base,中,who(),First_d,中,who(),以及,Second_d,中,who(),这在编程时非常有用。,实际上,这表示了一个动态性质,函数调用,p-who(),依赖于运行时,p,所指向对象。虚函数提供就是这种解释机制。假如在,base,中将组员函数,who(),说明为虚函数,则修改上述程序为例,5-29,虚函数是在基类中被冠以,virtual,组员函数,它提供了一个接口界面。虚函数能够在一个或多个派生类中被重新定义,但要求在派生类中 重新定义时,,虚函数函数原型,包含返回类型,函数名,参数个数,参数类型次序,必须完全相同。,第22页,class First_d:public Base,public:,First_d(int a):Base(a),void who(),“First derivation“x“n”;,;,#,include,class Base,protected:,int x;,public:,Base(int a),x=a;,virtual,void who(),cout“base ”x“n”;,;,class Second_d:public Base,public:,Second_d(int a):Base(a),void who()“Second derivation “xwho();,p=,p-who();,p=,p-who();,first_obj.who();,second_obj.who();,程序输出:,base 1,First derivation 2,Second derivation 3,First derivation 2,Second derivation 3,第23页,second_obj,x,3,Base:who(),Second_d:who(),first_obj,x,2,Base:who(),first_d:who(),p,base_obj,x,1,virtual Base:who(),基类虚函数,who(),定义了一个接口,在派生类中此接口定义了不一样实现版本,因为虚函数解释机制,实现了“单界面,多实现版本”思想。这种在运行时刻将函数界面与函数不一样实现版本进行匹配过程,称为晚期匹配,也称为运行时多态性。,p-who(),第24页,基类函数,f,含有虚特征条件是:,1),在基类中,将该函数说明为,virtual,函数。,2),定义基类公有派生类。,3),在基类公有派生类中原型一致地重载该虚函数。,4),定义指向基类指针变量,它指向基类公有派生类对象。,例,5-30,第25页,void main(),derived d;,base*bp=,bp-vf1();,bp-vf2();,bp-f();,class,base,public,:,virtual void,vf1();,virtual void,vf2();,virtual void,vf3();,void,f();,;,class,derived:public base,public,:,void,vf1();,void,vf2(int);,char,vf3();,void,f();,;,错误,仅返回类型不一样,含有虚特征,普通函数重载,参数不一样,虚特征丢失,普通函数重载,非虚函数重载,第26页,例,5-31,#,include,class,figure,protected,:,double,x,y;,public,:,void,set_dim(double i,double j=0),x=i;y=j;,virtual void show_area(),cout“No area,computation defined”;,cout“for this class.n”;,;,class,triangle:,public,figure,public,:,void show_area(),cout“Triangle with high”;,coutx“and base”y;,cout“has an area of”;,coutx*0.5*y“n”;,;,class,square :,public,figure,public,:,void show_area(),cout“Square with dimension”;,coutx“*”y;,cout“has an area of”;,coutx*y“n”;,;,class,circle :,public,figure,public,:,void show_area(),cout“Circle with radius”;,coutx;,cout“has an aera of”;,coutset_dim(10.0,5.0);,p-show_area();,p=,p-set_dim(10.0,5.0);,p-show_area();,p=,p-set_dim(9.0);,p-show_area(),;,程序输出,:,Triangle with high 10 and base 5 has an area of 25.0,Square with dimension 10*5 has an area of 50.0,Circle with radius 9 has an area of 254.34,X y,Figure:show_area(),triangle:show_area(),X y,Figure:show_area(),square:show_area(),X y,Figure:show_area(),circle:show_area(),p,第28页,2.,能够使用组员名限定能够强制使用静态联编,例,5-32,#,include,class,A,public,:,virtual void,fun()cout,“,In A,”,end1;,class,B:,public,A,public,:,void,fun()cout,“,In B,”,end1;,class,C:,public,B,public,:,void,fun()cout,“,In C,”,fun();,B,Bref.fun();,Bref.B:fun();,调用,B:fun(),不是,C:fun(),使用组员名限定能够强制使用静态联编,第29页,2026/7/21 周二,30,3.,在组员函数中调用虚函数,在一个基类或派生类组员函数中,能够直接调用等级中虚函数。此时,需要依据组员函数中,this,指针和它所指向对象来判断调 用是哪个函数。,例,5-33,第30页,#include,class,A,public,:,virtual void,fun1(),cout“A1-2”endl;,fun2();,virtual void,fun2(),cout“A2-3”endl;,fun3();,virtual,void,fun3(),cout“A3-4”endl;,fun4();,virtual void,fun4(),cout“A4-5”endl;,fun5();,virtual void,fun5(),cout“A end”endl;,;,class,B:,public,A,public,:,void,fun1()cout“B 1-2”;fun2();,void,fun2()cout“B 2-3”;fun3();,void,fun3()cout“B 3-4”;fun4();,void,fun4()cout,“,B 4-5,”,;fun5();,void,fun5()cout,“,B end,”,fun1();,Apointer2-fun1();,delete Apointer1;,delete Apointer1;,程序输出:,A 1-2,A 2-3,A 3-4,A 4-5,A end,B 1-2,B 2-3,B 3-4,B 4-5,B end,fun2(),相当于,fun2(const A*this),this,即为,Apointer2,,所以依然调用所指向对象中函数,第31页,例,5-34,#include,class,A,public,:,void,fun1()cout,“,A1-2,”,endl;fun2();,virtual void,fun2()cout,“,A2-3,”,endl;fun3();,virtual void,fun3()cout,“,A3-4,”,endl;fun4();,virtual void,fun4()cout,“,A4-5,”,endl;fun5();,virtual void,fun5()cout,“,A end,”,endl;,class,B:public A ,public:,void,fun1()cout“B 1-2”endl;fun2();,void,fun2()cout“B 2-3”endl;fun3();,void,fun3()cout“B 3-4”endl;fun4();,void,fun4()cout“B 4-5”endl;fun5();,void,fun5()cout“B end”fun1();,delete,Apointer;,程序输出:,A 1-2,B 2-3,B 3-4,B 4-5,B end,fun1(),不是虚函数,故基类指针变量,指向派生类时只能访问基类中定义组员。,fun2,,,fun3,fun4,fun5,是虚函数,故基类指针变量,指向派生类时访问是派生类中定义组员。,第32页,例,5-35#,include,class,A,public,:,virtual void,fun1()cout“A1-2”endl;fun2();,virtual void,fun2()cout“A2-3”endl;fun3();,virtual void,fun3()cout“A3-4”endl;fun4();,virtual void,fun4()cout“A4-5”endl;fun5();,virtual void,fun5()cout“A end”endl;,class,B:,public,A,public,:,void,fun3()cout“B 3-4”endl;fun4();,void,fun4()cout“B 4-5”fun1();,delete,Apointer;,程序输出:,A 1-2,A 2-3,B 3-4,B 4-5,A end,基类即使将,fun1,fun2,定义为虚函数,但在派生类中并没有原型一致重载它们,所以要调用基类中函数。,第33页,2026/7/21 周二,34,4.,在结构函数和析构函数中调用虚函数,在结构函数和析构函数中调用虚函数时,采取静态联编,。即它们所调用虚函数是自己类或者它基类中虚函数,但不是任何在派生类中定义虚函数。,例,5-36,第34页,#include,class A,public:,A()cout“A is Creating”endl;,virtual void fun1()cout“A fun1”endl;,virtual void fun2()cout“A fun2”endl;,A()cout“A is Destroy”endl;,class B:public A,public:,B()cout“B is Creating”endl;,fun1();,void fun()fun1();,B()cout“B is destroy”end1;fun2();,class C:public B,public:,C()cout“C is Creating”endl;,void fun1()coutC fun1”endl;,virtual void fun2()coutC fun2”endl;,C()cout“C is Destroy”endl;,fun2();,void main(),C Cobj;,Cobj.fun();,程序输出:,A is Creating,B is Creating,A fun1,C is Creating,C fun1,C is destroy,C fun2,B is destroy,A fun2,A is destroy,在结构函数和析构函数中调用虚函数时,,是自己类或者它基类中虚函数,但不是任何在派生类中定义虚函数。,第35页,2026/7/21 周二,36,5.,析构函数能够定义为虚函数,结构函数不能为虚函数,而析构函数能够定义为虚函数。,若析构函数为虚函数,那么当使用,delete,释放基类指针指向派生类对象时,先调用派生类析构函数,再调用基类析构函数。,第36页,class,Deriver:,public,Base,int,d;,public,:,Deriver(int num1,int num2):Base(num1)d=num2;cout“Deriver createn”;,Deriver(),cout“Deriver destoryn”;,;,例,5-37,#,include,class,Base,int,b;,public,:,Base(int num),b=num;cout“Base createn”;,Base(),cout“Base destroyn”;,;,void,main(),Base*pb1,*pb2;,pb1=,new,Base(1);,pb2=,new,Deriver(2,3);,delete,pb1;,delete,pb2;,cout“*n”;,Base Bobj(4);,Deriver Dobj(5,6);,程序输出,:,Base create,Base create,Deriver create,Base destroy,Base destory,*,Base create,Base create,Deriver create,Deriver destroy,Base destroy,Base destory,基类对象指向派生类对象时,释放时不调用派生类析构函数,第37页,例,5-38,#,include,class,Base,int,b;,public,:,Base(int num),b=num;cout“Base createn”;,virtual,Base(),cout“Base destroyn”;,class,Deriver:public Base,int,d;,public,:,Deriver(,int,num1,int,num2):Base(num1),d=num2;cout“Deriver createn”;,Deriver(),coutf();/,调用,A2:f(),pa4-f();/,调用,A2:f(),继承路径如图,:,A1,A2,A3,A4,A1():,A1(),A3():,A2(),A3(),A4():,A4(),创建,A4,对象时,结构函数调用次序为,:,第40页,因为,A2,是,A1,派生类,,A2,中重新定义函数,f,覆盖了类,A,中定义函数,f,而且,A4,和,A2,都将,A1,说明为虚基类,所以,,pa4-f(),调用是,A2:f(),虚基类也能应用这个规则。,有些人可能会想,,A1:f(),离,A4,更近,因为,A1,是,A4,直接基类,而,A2,不是。,pa4-f(),应该调用,A1:f(),而不是调用,A2:f(),。情况并非如此,由,DAG,图可见,依据继承路径,pa4-f(),应有两种调用选择,:,A2:f();,和,A1:f(),第41页,2026/7/21 周二,42,5.3.3,纯虚函数及抽象类,基类表示抽象概念,如,figure,是一个基类表示有型东西,能够派生出封闭图形和非封闭图形两类。,Shape,表达了一个抽象概念,在,figure,中定义一个求面积函数显然是无意义,但能够将其申明为一个虚函数,提供一个派生公共界面,并由各派生类提供求面积各自版本。所以基类有些虚函数没有定义是很正常,不过要求派生类必须重新定义这些虚函数。,为此,C+,引入了纯虚函数概念。,第42页,2026/7/21 周二,43,纯虚函数是一个在基类中说明虚函数它在基类中没有定义,要求任何派生类必须定义自己版本。,纯虚函数含有以下形式:,virtual type func_name(,参数表,),0,;,在结构函数和析构函数中调用虚函数使用静态编联,所以在这两个函数中不能调用纯虚函数。,但其它函数能够调用纯虚函数。,第43页,2026/7/21 周二,44,假如一个类最少有一个纯虚函数,就称这个类为抽象类。,抽象类能够定义一个接口,由派生类提供各种实现。,抽象类只能用作其它类基类,.,能够用作申明抽象类指针和引用。,不能创建对象。,不能用作参数,不能用作函数返回类型或显式转换类型。,第44页,例,5-40,class point,;,class shape,point center;,public:,point where(),return center;,void move(point p),center=p;draw();,virtual void rotate(int)=0;,virtual void draw()=0;,;,shape x;,shape*p;,shape fun();,void g(shape);,x=shape(23);,shape,错误,抽象类不能建立对象,能够申明抽象类指针,错误,抽象类不能作为返回类型,错误,抽象类不能作为参数类型,能够申明抽象类引用,不能用作显式类型转换,第45页,2026/7/21 周二,46,假如派生类没有原型一致地重载该纯虚函数。,从基类继承来纯虚函数,在派生类中仍是纯虚函数。,比如,:,class,ab_circle :public shape,int,radius;,public,:,void,rotate(int);,;,因为,shape:draw(),是一个纯虚函数,缺省,ab_cricle:draw,也是一个纯虚函数,这时,ab_circle,仍为抽象类。,第46页,2026/7/21 周二,47,要使,ab_circle,类为非抽象,必须以下说明:,class,ab_circle :,public,shape,int,radius;,public,:,void,rotate(int);,void,draw(),;,第47页,1.,以下选项中正确是,A),结构函数能够重载,析构函数不能重载,B),结构函数不能重载,析构函数能够重载,C),结构函数能够重载,析构函数也能够重载,D),结构函数不能重载,析构函数也不能重载,2.,类析构函数作用是,A),普通组员函数,B),类初始化,C),对象初始化,D),删除对象,3.,对友元函数正确描述是,A),友元函数实现必须在类内部定义,B),友元函数是类组员函数,C),友元函数拨坏了破坏了类封装性和隐藏性,D),友元函数不能访问类私有组员,4.,在,C+,中,数据封装要处理问题是,A),数据规范化,B),便于数据转换,C),防止数据丢失,D),预防不一样模块之间数据访问,5,对结构体中定义组员,默认访问权限为,A)public B)protected C)private D)static,第48页,6.,类型转换函数作用是,7,赋值重载函数与赋值结构函数应用中区分是,8 C+,中局部变量和全局变量重名时,欲访问全局变量,应该,9,以下程序运行时会出现严重错误,为何?,#include,void main(),char*p,*q;,p=new char10;,q=new char10;,strcpy(p,ABCD);,q=p;delete p;delete q;,10,类中保护段数据与共有段数据主要区分是什么?,第49页,10.C+,建立类族实现是经过,A),类嵌套,B),虚函数,C),类继承,D),抽象类,11.,不能被派生类继承有,结构函数,B),虚函数,C),静态组员函数,D),赋值操作函数,1.,纯虚函数是,A)virtual int vf(int),;,B)void vf(int)=0;,C)virtual void vf()=0;D)virtual void vf(int),2.,设置虚基类目标是,A),简化程序,B),消除二义性,C),提升运行效率,D),降低目标代码,3,设置虚函数目标是实现动态编联,用关键字,标识虚函数。,4,类派生有主要有三种方式,分别用描述符,public,protected,和,表示。,第50页,
展开阅读全文

开通  VIP、SVIP  下载更划算
下载10份以上建议开通 VIP 会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        关注我们

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服