第十一章 多态Word文档下载推荐.docx
- 文档编号:3285285
- 上传时间:2023-05-01
- 格式:DOCX
- 页数:19
- 大小:52.48KB
第十一章 多态Word文档下载推荐.docx
《第十一章 多态Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《第十一章 多态Word文档下载推荐.docx(19页珍藏版)》请在冰点文库上搜索。
在前面提供的文法中是允许派生类重载基类成员函数的,对于类的重载来说,明确的,不同类的对象,调用其类的成员函数的时候,系统是知道如何找到其类的同名成员,上面代码中的a.ShowMember(),即调用的是Vehicle:
ShowMember(),b.ShowMember(),即调用的是Car:
ShowMemeber()。
但是在实际工作中,很可能会碰到对象所属类不清的情况,下面我们来看一下派生类成员作为函数参数传递的例子,代码如下:
inttotal;
voidtest(Vehicle&
temp)
temp.ShowMember();
}
test(a);
test(b);
在这个例子中,对象a与b分别是基类和派生类的对象,而函数test的形参却只是Vehicle类的引用,按照类继承的特点,系统把Car类对象看做是一个Vehicle类对象,因为Car类的覆盖范围包含Vehicle类,所以test函数的定义并没有错误,我们想利用test函数达到的目的是,传递不同类对象的引用,分别调用不同类的,重载了的,ShowMember成员函数,但是程序的运行结果却出乎人们的意料,系统分不清楚传递过来的基类对象还是派生类对象,无论是基类对象还是派生类对象调用的都是基类的ShowMember成员函数。
因为在执行test(b)时,对象b先被裁剪成了Vehicle类对象。
为了要解决上述不能正确分辨对象类型的问题,我们可以采用多态性(polymorphism)的技术来解决,对于示例程序1,这种能够在编译时就能够确定哪个重载的成员函数被调用的情况在C++中被称为先期联编(earlybinding),而在系统能够在运行时,能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性,在C++中也被称为滞后联编(latebinding)。
下面我们要看的例子,就是滞后联编,滞后联编正是解决多态问题的方法。
代码如下:
speed=speed;
total=total;
virtualvoidShowMember()//虚函数
aird=aird;
virtualvoidShowMember()//虚函数
多态特性的工作依赖虚函数的定义,在需要解决多态问题的重载成员函数前,加上virtual关键字,那么该成员函数就变成了虚函数,从上面的示例代码运行的结果看,系统成功的分辨出了对象的真实类型,成功的调用了各自的重载成员函数。
多态特性让程序员省去了细节的考虑,提高了开发效率,使代码大大的简化。
11.2.多态的实现
当然虚函数的定义也是有缺陷的,因为计算机为了实现多态特性必须增加一些数据存储和执行指令的开销。
虚函数的定义要遵循以下重要规则:
1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。
2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。
3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。
4.构造函数不能是虚函数,因为构造的时候,对象还是一片位定型的空间,只有构造完成后,对象才是具体类的实例。
5.析构函数可以是虚函数,而且通常声名为虚函数。
11.2.1虚函数的表示
为了表示虚函数,我们要做以下工作:
1.增加关键字virtual,用来表示与普通成员函数的不同
2.增加虚函数表Vitualtable,用来记录类中所有虚函数的入口地址;
根据函数地址调用正确的函数
3.增加虚指针Vpointer(vptr),用来指向虚函数表,从而可以在运行的时候查找虚函数代码的地址。
11.2.2虚函数的实现
为了实现虚函数,在给含有虚函数的类对象分配空间的时候需要分配虚指针指针的空间,用来指向虚函数表。
如下图11.1所示。
图11.1虚函数的实现
11.3文法的改变
为了支持类多态,修改的文法如下:
modifier-list→{virtual|public|private|protected|empty}
modifier→virtual|public|private|protected|empty
11.3.1词法分析
为了支持类多态,在词法分析程序中添加与类多态相关的关键字:
virtual。
11.3.2语法分析
在加入对类多态的支持后,抽象语法树并未有所变化。
11.3.3符号表设计与构造
在符号表设计中,修改后的符号表如下所示:
1.FunInfo符号表中的isvirtual字段:
用来标明一个函数是否为虚函数,其值为1是该函数是虚函数,为-1时不是虚函数。
2.ClassInfo符号表中的virtualfun_table字段:
为每个带有虚函数的类构造一个虚函数表virtualfun_table,在virtualfun_table中按顺序存储每个虚函数的相关信息(包括函数代码的入口地址信息)。
3.ClassInfo符号表中的vfptr字段:
指向虚函数表,从而可以在运行的时候查找虚函数代码的地址。
在给类对象分配空间的时候需要分配这样一个指针的空间大小,用来存放虚指针。
4.ClassInfo符号表中的virtualfun_count字段:
其值为类中虚函数的个数。
增加的符号表有虚函数表VirtualfunItem,其中三个字段为:
1.vfun_name:
虚函数名。
2.entry_addr:
虚函数的入口地址。
3.rva:
虚函数的偏移量,用于表明虚函数的先后次序
修改后的符号表如下图11.2所示:
图11.2修改后的符号表
有了上面的符号表修改后的情况,我们接下来用一个例子给出具体说明:
Classshape{
inta;
virtualvoiddraw(){a=0;
“shape”<
}
Classline:
classshape{
Voiddraw(){a=1;
Classcircle:
Voiddraw(){a=2;
具体如图11.3所示:
图11.3符号表
由上图可见,对虚函数的调用变成了对函数指针的调用
到这里,我们知道了为了支持多态性,符号表中所要增加的字段,以及各字段所起的作用。
下面,我们介绍何时对这些字段赋值。
这部分工作有一部分在语义分析过程中完成,其具体实现请同学们参照有关代码,这里只给出讲解。
实现代码在文件analyzeLib/symtab.cs中。
在语义分析,构建符号表调用函数buildSymtable时,遇到ClassDecl节点,就调用processClass对类声明进行处理。
如果抽象语法树节点的IsVirtual值为1,则isvirtual赋值为1,表示该成员函数为虚函数,具体代码如下。
同样也是在语义分析,构建符号表调用函数buildSymtable时,遇到ClassDecl节点,就调用processClass对类声明进行处理。
如果有基类,并且基类中有虚函数,则需处理虚函数表VirtualfunItem,然后修改virtualfun_table字段。
接下来处理到成员函数定义节点FunDecl时,若tmpNode.IsVirtual==1,则需首先处理虚函数表VirtualfunItem,然后将virtualfun_count加1,最后修改virtualfun_table字段。
对于vfptr,是在代码生成的时候给出其内容的,实现代码在文件codegen/CodeGen.cs中。
其中涉及的函数主要有两个,分别是preProcess和procTree,procTree首先调用preProcess,在函数preProcess中,返填类符号表中虚函数表的入口地址vfptr。
11.3.4代码生成
在调用虚函数时,通过this指针找到vfptr,然后找到vfptr所指向虚表的第一个元素的内容,即第一个虚函数的入口地址,即可访问所要调用的虚函数。
例如:
Voidfoo(shapes){
s.draw();
对于最后三行代码生成如下目标代码:
Fooprocnear
…
Movax,[bp-1]
Pushax;
传递this指针
//通过查找vtable得到需要
调用函数的入口地址
//Call_V_draw;
Movax,[ax]
Callax
…
Ret
11.3.5运行情况
例如程序classPet
{
intpname;
Pet(intx,inty)
{
this.pname=x;
this.a=y;
virtualpublicintName()
returnpname;
virtualpublicintIncrease()
this.a=this.a+1;
returna;
~Pet()
classDog:
publicPet
intdname;
Dog(intx,inty,intz)
pname=x;
a=y;
dname=z;
publicintName()
returndname;
~Dog()
voidmain(void)
intresult1,result2,result3;
intname;
Petpet;
pet=newPet(1,2);
result1=pet.Increase();
/*普通的成员函数调用*/
/*继承*/
Dogdog1;
dog1=newDog(3,4);
result2=dog1.Increase();
/*调用父类的成员函数*/
/*多态*/
Petdog2;
dog2=newDog(3,4,5);
name=dog2.Name();
/*虚函数调用*/
deletepet;
deletedog1;
deletedog2;
return;
}:
生成的目标代码是:
line0:
virtualfun:
PUSH79
line1:
PUSH31
line2:
PUSH20
line3:
line4:
CALLNEARPTR_MAIN
line5:
HALT
line6:
PetPet:
PET_PETPROCNEAR
line7:
ADDSP4
line8:
MOV[BP+2]0
line9:
MOV[BP+3]0
line10:
MOVAX[BP]
line11:
ADDAX4
line12:
MOV[BP+4]AX
line13:
MOV[BP+5]SP
line14:
MOVAX[BP-1-0+0]
line15:
MOV_[0+1+0]AX
line16:
MOVAX[BP-1-1+0]
line17:
MOV_[1+1+0]AX
line18:
RET
line19:
PET_PETENDP
line20:
PetName:
PET_V_NAMEPROCNEAR
line21:
line22:
line23:
MOV[BP+3]1
line24:
line25:
line26:
line27:
line28:
MOVAX_[0+1+0]
line29:
line30:
PET_V_NAMEENDP
line31:
PetIncrease:
PET_V_INCREASEPROCNEAR
line32:
line33:
line34:
MOV[BP+3]2
line35:
line36:
line37:
line38:
line39:
MOVAX1
line40:
MOVBXAX
line41:
MOVAX_[1+1+0]
line42:
ADDAXBX
line43:
line44:
line45:
line46:
PET_V_INCREASEENDP
line47:
Pet~Pet:
PET_~PETPROCNEAR
line48:
line49:
line50:
MOV[BP+3]3
line51:
line52:
line53:
line54:
line55:
line56:
PET_~PETENDP
line57:
DogDog:
DOG_DOGPROCNEAR
line58:
MOVAX[BP-2+0]
line59:
PUSHAX
line60:
MOVAX[BP-2+1]
line61:
line62:
CALLNEARPTR_PET_PET
line63:
SUBSP2
line64:
line65:
line66:
MOV[BP+3]4
line67:
line68:
line69:
line70:
line71:
line72:
line73:
line74:
line75:
MOVAX[BP-1-2+0]
line76:
MOV_[2+1+0]AX
line77:
line78:
DOG_DOGENDP
line79:
DogName:
DOG_NAMEPROCNEAR
line80:
line81:
line82:
MOV[BP+3]5
line83:
line84:
line85:
line86:
line87:
MOVAX_[2+1+0]
line88:
line89:
DOG_NAMEENDP
line90:
Dog~Dog:
DOG_~DOGPROCNEAR
line91:
line92:
line93:
MOV[BP+3]6
line94:
line95:
line96:
line97:
line98:
CALLNEARPTR_PET_~PET
line99:
line100:
DOG_~DOGENDP
line101:
main:
MAINPROCNEAR
line102:
ADDSP11
line103:
line104:
MOV[BP+3]7
line105:
MOVAX-1
line106:
line107:
line108:
NEW[BP+6+4]PET
line109:
line110:
NEW[BP+6+4+0]PET
line111:
MOVAX2
line112:
line113:
line114:
line115:
line116:
line117:
MOVTHIS[BP+6+4+0]
line118:
VCALLNEARPTR_PET_INCREASE
line119:
MOV[BP+6+0+0]AX
line120:
NEW[BP+6+5]DOG
line121:
CALLNEARPTR_DOG_DOG
line122:
NEW[BP+6+5+0]DOG
line123:
MOVAX4
line124:
line125:
MOVAX3
line126:
line127:
line128:
line129:
MOVTHIS[BP+6+5+0]
l
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第十一章 多态 第十一