下载

3下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 C++课件--薛景瑄chapter_4

C++课件--薛景瑄chapter_4.doc

C++课件--薛景瑄chapter_4

jingernanhang
2018-09-05 0人阅读 举报 0 0 暂无简介

简介:本文档为《C++课件--薛景瑄chapter_4doc》,可适用于IT/计算机领域

第四章继承性(inheritance)与类的派生(derivation)派生类(derivedclass)及其对象(object)定义第三章中提到在面向对象程序设计语言中“类”是一组具有相同数据结构和相同操作(方法、函数)的集合是一系列具有相同性质的对象的抽象内容它描述的不是个别对象而是全体对象的共同特征。“类”是具有相同共性的各事物的集合是这些事物的统一抽象内容。C中“类”被表达为一个具有特定功能的程序块它提供代码共享(代码重用性)以便用户可以方便地建立所需要的任何数据类型。但一个“类”(基类)无法包含这些事物的全部共性而只包含主要共性。为包含其它次要共性可通过继承机制使用其它类(称为派生类)。继承是C面向对象程序设计的重要特性之一。所谓继承是建立一个新的类(即直接派生类)从一个或多个已经定义的类(称为直接基类)中继承一部分或全部函数和数据同时还能重新定义或增加新的数据和函数。继承机制在对象之间建立了派生关系从而建立类的层次或等级(hierarchyofclasses)。引入继承机制的目的是实现代码重用性(reusability):一方面可以重用先前的代码避免不必要的重复设计另一方面如果原代码不能完全满足要求可以在绝不改变原有代码的情况下补充新的代码增加新的功能。请见下图:上图中“几何图形”是基类它包含了几何图形的一些基本共性。它的派生类“三角形”、“四边形”和“圆形”各自包含了本图形的一些次要共性。例如计算面积的表达式这三个图形都不相同。而“四边形”又可派生出“正方形”、“平行四边形”和“矩形”从而使它们各自具有不同属性。又例如先有一个类为:classstring{intlengthchar*contentspublic:string()intgetlength()}过些时候需要增加功能设计一个新类:classeditstring{intlengthchar*contentsintcursorpublic:editstring()intgetlength()intgetcursorpos()}从中看出这两个类的内容有不少重复。后一个类应该可以继承前一个类的全部或一部分内容(在后一个类中用红色标出)。可以将前一个类classstring定义为基类(baseclass):然后另外定义一个类stringderived称为派生类(derivedclass)如下:claa="<<a<<endl}}classA{intapublic:A(intj){a=jcout<<"ConstructorA"<<a<<endl}voidPrint(){cout<<"a="<<a<<endl}}classA{intapublic:A(intk){a=kcout<<"ConstructorA"<<a<<endl}voidPrint(){cout<<"a="<<a<<endl}}classD:publicA,publicA此顺序决定基类部分构造函数的调用顺序{intdAaapublic:D(inti,intj,intk,intl):A(i),A(j),aa(k)初始化列表顺序与构造函数调用顺序无关{d=lcout<<"ConstructorD"<<d<<endl}voidPrint(){A::Print()A::Print()aaPrint()cout<<"d="<<d<<endl}}voidmain(){Ddd(,,,)ddPrint()D*ptrd=ddint*ptr=(int*)ptrdcout<<"firstpartofddis"<<*ptr<<endlcout<<"secondpartofddis"<<*ptr<<endlcout<<"thirdpartofddis"<<*ptr<<endlcout<<"fourthpartofddis"<<*ptr<<endl以下用作§"派生类对象为基类对象赋值"第二例cout<<"以下派生类对象dd分别为基类对象aa和aa赋值"<<endlAaa()aa=dd派生类对象dd为基类对象aa赋值aaPrint()Aaa()aa=dd派生类对象dd为基类对象aa赋值aaPrint()}*Results:ConstructorAConstructorAConstructorAConstructorDa=a=a=d=firstpartofddissecondpartofddisthirdpartofddisfourthpartofddis以下派生类对象dd分别为基类对象aa和aa赋值ConstructorAa=ConstructorAa=*DAGpublicpublic{Aaa}构造函数调用顺序:AAaaD参数传递顺序:ADAaaclassD对象dd的栈区存储内容主程序第一句:Ddd(,,,)dda=dda=ddd=ddaaa=对象dd主程序第十句:Aaa()aaa=对象aa主程序第十一句:aa=ddaaa=对象aa主程序第十三句:Aaa()aaa=对象aa主程序第十四句:aa=ddaaa=对象aa此程序中classD对象dd的栈区存储内容的排列顺序是和各类构造函数的调用顺序不同的因栈区存储内容的排列顺序(主要是classD中数据的排列顺序)只决定于classD中数据的声明顺序。如果classD中数据的声明顺序改为如下:classD:publicA,publicA{Aaaintd……}则classD对象dd的栈区存储内容将成为:dda=dda=ddaaa=ddd=*二义性(Ambiguity)(一)访问不同基类中的相同成员时可能出现的二义性例ambigcppToseehowtoavoidambiguityinprinting'inta'有错无法运行#include<iostreamh>classbaseA{public:intabaseA(inti){a=i}}classbaseB{public:intabaseB(inti){a=i}}classderive:publicbaseA,publicbaseB{public:derive(inti):baseA(i),baseB(i){}}voidmain(){deriveobj()cout<<obja<<endlerror:'derive::a'isambiguous}以上程序中两个基类都有数据成员intaobja无法规定是哪一个a。因此出现二义性。主函数应修改为:ambigcppToseehowtoavoidambiguityindisplaying'inta'…………voidmain(){deriveobj()cout<<objbaseA::a<<endlcout<<objbaseB::a<<endl}*Results:*其中初始化列表为:derive(inti):baseA(i),baseB(i)(二)访问共同基类中的成员时可能出现的二义性例具有单个数据成员的三层继承ambigcppToseehowtoavoidambiguityindisplaying'inta'有错无法运行#include<iostreamh>classbase{public:intabase(inti){a=i}}classderive:publicbase{public:derive(inti):base(i){}}classderive:publicbase{public:derive(inti):base(i){}}classgrand:publicderive,publicderive{public:grand(inti):derive(i),derive(i){}}voidmain(){grandg()cout<<ga<<endlerror:'grand::a'isambiguous}以上程序的DAG中存在两个classbase只提ga无法明确指出是哪一个classbase的a。主函数应修改如下:ambigcppToseehowtoavoidambiguityindisplaying'inta'…………voidmain(){grandg()cout<<gderive::a<<endlyoumaynotuse'gderive::base::a',otherwiseERROR:'base'isnotamemberof'derive'andERROR:'grand::a'isambiguouscout<<gderive::a<<endl}*Results:*顺便看一下各初始化列表为:derive(inti):base(i)derive(inti):base(i)grand(inti):derive(i),derive(i)。例具有多个数据成员的三层继承consordercppToseetheorderofinvokingconstructorsinanobjectofamultiinheritedclass#include<iostreamh>classbase{public:base(inti=){a=icout<<"CONSbase"<<endl}inta}classderive:publicbase{intbpublic:derive(inti=){b=icout<<"CONSderive"<<endl}}classderive:publicbase{intbpublic:derive(inti=){b=icout<<"CONSderive"<<endl}}classgrand:publicderive,publicderive{intcpublic:grand(inti=){c=icout<<"CONSgrand"<<endl}}voidmain(){grandobjgcout<<"Sizeofclassgrand:"<<sizeof(grand)<<endlgrand*ptrg=objgint*ptr=(int*)ptrgcout<<"firstpartofobjgis"<<*ptr<<endlcout<<"secondpartofobjgis"<<*ptr<<endlcout<<"thirdpartofobjgis"<<*ptr<<endlcout<<"secondpartofobjgis"<<*ptr<<endlcout<<"thirdpartofobjgis"<<*ptr<<endl}*Result:CONSbaseCONSderiveCONSbaseCONSderiveCONSgrandSizeofclassgrand:firstpartofobjgissecondpartofobjgisthirdpartofobjgissecondpartofobjgisthirdpartofobjgis*DAGpublicpublicpublicpublic参数传递顺序:derivebasegrand()()()derivebase()()构造函数调用顺序:basederivebasederivegrandclassgrand的对象objg的栈区存储内容objgderive::a=objgderive::b=objgderive::a=objgderive::b=objgc=classgrand对象objg的栈区存储内容是按照程序运行结果画出的。虚基类(virtualbaseclass)功能和概念§的“(二)访问共同基类中的成员时可能出现的二义性”一节的例“具有单个数据成员的三层继承”中的程序ambigcpp中出现两个共同基类classbase。这是不希望出现的。经常要求DAG中只出现一个classbase如下图。这就得求助于虚基类。DAGpublicpublicpublicpublic为此可将classbase定义为虚基类。其方式是在其派生类的定义中在基类名称前加上virtual关键词即可。如以下程序virclscpp中所示:例virclscppUsevirtualclasstoavoidambiguityindisplaying'inta'#include<iostreamh>classbase{public:base(inti){a=icout<<"CONSB"<<endl}inta}classderive:virtualpublicbase{public:derive(inti):base(i){cout<<"CONSD"<<endl}}classderive:virtualpublicbase{public:derive(inti):base(i){cout<<"CONSD"<<endl}}classgrand:publicderive,publicderive{public:grand(inti):derive(i),derive(i),base(i){cout<<"CONSG"<<endl}}voidmain(){grandg()cout<<"ga="<<ga<<endl}*Results:CONSBCONSDCONSDCONSGga=*以上程序的运行结果显示classbase的构造函数只调用一次而不是两次因此得到以上所示DAG。程序中在classbase的派生类的继承方式前加上virtual关键词,从而声明classbase为虚基类。在这样的DAG中由于只有一个classbase此时ga不会产生二义性。还有一个问题:程序运行结果不是以前程序中的或而是,而这个参数是从classgrand构造函数的初始化列表中base(i)传递给classbase的为什么?这涉及向虚基类传递参数的问题将在§中分析。初始化构造函数的调用顺序虚基类部分的初始化和一般多继承中基类部分的初始化在语法上是相同的但构造函数的调用顺序不同服从以下规则:同一层中先调用虚基类部分的构造函数。同一层中存在多个虚基类时按照各虚基类的声明顺序来调用。如虚基类V由非虚基类B派生而来则先调用基类B的构造函数然后调用派生类V的构造函数。例虚基类和非虚基类同时存在的程序中构造函数的调用顺序。virclsconscppToseetheorderofinvocationofconstructorswhentherearevirtualclasses#include<iostreamh>classbasev{public:basev(){cout<<"CONSbasev"<<endl}}classbase{public:base(){cout<<"CONSbase"<<endl}}classderive:base,virtualbasev{public:derive(){cout<<"CONSderive"<<endl}}classderivev:base,virtualbasev{public:derivev(){cout<<"CONSderivev"<<endl}}classgrand:derive,virtualderivev此处virtual并非必要只为了教学需要用于说明不同类构造函数的调用顺序{public:grand(){cout<<"CONSgrand"<<endl}}voidmain(){grandobj}*Results:CONSbasevCONSbaseCONSderivevCONSbaseCONSderiveCONSgrand*classgrand的DAG其中粗连线表示虚继承、、、、、表示构造函数调用顺序。在分析派生类构造函数调用顺序时仍然使用王燕书p总结的“先祖先再客人后自己”的规则。先从最底层派生类grand开始。它有两个祖先即两个直接基类其中应先调用虚基类derivev的构造函数。对于derivev而言也应执行“先祖先再客人后自己”。同样它的两个直接基类中有一个虚基类basev应先调用它的构造函数CONSbasev。然后调用CONSbase再回到derivev自己调用CONSderivev。然后轮到classderive也是执行“先祖先再客人后自己”。其祖先是classbase(因classbase不是虚基类所以出现两次)。调用完CONSbase后再调用CONSderive。最后调用CONSgrand。例基类为虚基类、派生类具有对象成员(子对象)时构造函数的调用顺序virclscpp#include<iostreamh>classA{intiapublic:A(inti){ia=icout<<"A::"<<ia<<endl}}classB:virtualpublicA{public:B(charchb,inti):A(i){cout<<"B::"<<chb<<endl}}classC:virtualpublicA{public:C(charchc,inti):A(i){cout<<"C::"<<chc<<endl}}classM{public:M(doubledd){cout<<"M::"<<dd<<endl}}classJ{public:J(intij){cout<<"J::"<<ij<<endl}}classD:publicB,publicC{MmJ*ptrpublic:D(charcc,charcb,inti,doubledd,char*str):B(cb,i),C(cc,i),m(dd),A(i){cout<<"D::"<<str<<endl}}voidmain(){Bobjb('W',)Cobjc('H',)Dobjd('Z','P',,,"yes")}*Results:A::B::WA::C::HA::B::PC::ZM::因不建立classJ的对象所以不调用它的构造函数D::yes*classD的DAG{Mm}其中表示派生关系表示构造函数调用顺序表示参数传递(请注意:DAG中classA与classB和classC之间没有用于传递参数的蓝色箭头。这将在下面§中详述)但在classB和classC的对象objb和objc的DAG中在classA与classB和classC之间却存在着用于传递参数的蓝色箭头。如下:classB的DAGclassC的DAG向基类传递参数的问题(规则)(第一条)在没有虚基类的情况下只准逐级向上传递参数而不允许越级向间接基类传递参数(例如不允许从classz直接向classx传递参数)否则出现以下错误:ERROR:‘x’isnotabaseclassof‘z’infunctionz::z(int,int,…)例如:()的例中的initabcpp即:DAG参public数传private递()中例的initabcpp看前面它的DAG(第二条)在有虚基类的情况下虚基类的初始化参数只能够从最下层用于建立对象的类中直接传递上来。如在该最下层类(如下例中的classW)中不传递参数给虚基类(如下例中的classV)则将出现错误。但中间各类仍然需要向上传递参数的功能(当建立中间各类的对象时仍然有用)。请见下例:例三层派生类中的虚基类virclscppInitializationofargumentofvirtualbaseofthreelevels#include<iostreamh>classV{public:V(inti){v=i}intv}classX:virtualpublicV{public:X(inti,intj):V(i){x=j}intx}classY:virtualpublicV{public:Y(inti,intj):V(i){y=j}inty}classW:publicX,publicY{public:W(inta,intb,intc,intd,inte,intf):X(a,b),Y(c,d),V(e){w=f}intw}voidmain(){Wobj(,,,,,)cout<<objv<<endlcout<<objx<<endlcout<<objy<<endlcout<<objw<<endl}*Results:(neithernor)*DAG其中表示派生关系表示参数传递例五层派生类中的虚基类virclscppInitializationofargumentofvirtualbaseoffivelevelsofderivedclasses#include<iostreamh>classB{public:B(inti){b=i}intb}classV:publicB{public:V(inti,intj):B(i){v=j}intv}classX:virtualpublicV{public:X(inti,intj):V(i,j){x=j}intx}classY:virtualpublicV{public:Y(inti,intj):V(i,j){y=j}inty}classW:publicX,publicY{public:W(inta,intb,intc,intd,inte,intf):X(a,b),Y(c,d),V(e,f){w=a}intw}classZ:publicW{public:Z(inta,intb,intc,intd,inte,intf,intg):W(b,c,d,e,f,g),V(f,g){z=a}intz}voidmain(){Zobj(,,,,,,)cout<<objb<<endlcout<<objv<<endlcout<<objx<<endlcout<<objy<<endlcout<<objw<<endlcout<<objz<<endl}*Results:(nornornor)*DAG其中表示派生关系表示参数传递表示实际上没有参数传递但提供了可能性。以上各程序都说明存在虚基类时可以传递参数也即其构造函数可以具有参数。因此王燕书上p倒数第行“在一般情况下虚基类只允许定义不带参数的或带缺省参数的构造函数”的叙述是错误的。*内存存储内容使用虚基类后其栈区存储内容就复杂多了。原因是多继承派生类部分(上例中的classW)与虚基类部分(上例中的classV)之间存在着多条继承关系(至少两条)因此在建立虚基类的直接派生类(classX和classY)对象时必须考虑分别为两个对象(甚至更多个对象)确定继承关系。在处理这个问题时以最上层的基类(上例中为classB)为基准点。此最上层的基类可能就是虚基类(见例)也可能是虚基类的直接或间接基类(见例)。例最简单的例子:多继承派生类部分(classgrand)与虚基类部分(classvbase)之间只有两个类(classderx和classdery)总共四个类。contvclscppInitializationofargumentofvirtualbaseofthreelevelsofderivedclasses#include<iostreamh>classvbase{public:vbase(intj){vb=j}intvb}classderx:virtualpublicvbase{public:derx(inti,intj):vbase(i){dx=j}intdx}classdery:virtualpublicvbase{public:dery(inti,intj):vbase(j){dy=j}intdy}classmultigrand:publicderx,publicdery{public:multigrand(inta,intb,intc,intd,inte,intf):derx(a,b),dery(c,d),vbase(f){gr=c}intgr}voidmain(){multigrandobjg(,,,,,)cout<<"sizeof(objg):"<<sizeof(objg)<<endlint**ptr=(int**)objg指向派生类对象objgcout<<hex<<"Addrofobject:"<<ptr<<endlcout<<"Contentsofobj:"<<endlcout<<"Firstelement:"<<*ptr<<endlcout<<"Indexingoffirstelement:"<<**ptr<<endlcout<<"secondindexingoffirstelement:"<<*(*ptr)<<'h'<<endlptrcout<<"Secondelement:"<<(int)*ptr<<endlptrcout<<"Thirdelement:"<<*ptr<<endlcout<<"Indexingofthirdelement:"<<**ptr<<endlcout<<"secondindexingofthirdelement:"<<*(*ptr)<<'h'<<endlptrcout<<"Fourthelement:"<<(int)*ptr<<endlptrcout<<"Fifthelement:"<<(int)*ptr<<endlptrcout<<"Sixthelement:"<<(int)*ptr<<endlcout<<"Valuesofdata:"<<endlcout<<"objgvb="<<objgvb<<endlcout<<"objgdx="<<objgdx<<endlcout<<"objgdy="<<objgdy<<endlcout<<"objggr="<<objggr<<endl}*Results:sizeof(objg):Addrofobject:xFDEContentsofobj:Firstelement:xCIndexingoffirstelement:secondindexingoffirstelement:hSecondelement:Thirdelement:xCIndexingofthirdelement:secondindexingofthirdelement:chFourthelement:Fifthelement:Sixthelement:Valuesofdata:objgvb=objgdx=objgdy=objggr=*DAGpublicpublicpublicpublicclassmultigrand的对象objg的双区存储内容栈区数据区C(指针)(objgdx)C(指针)(objgdy)(objggr)(objgvb)在以上程序的双区存储内容中栈区存储内容除各基类部分和派生类部分的所有数据成员外还有两个指针指向数据区内两个数据块。这两个数据块的第一个数据是零值(其作用尚不明)作为重要数据的第二个数据是一个偏移量(分别为h和ch(h表示十六进制))分别表示这两个指针单元距离最上层的基类(即classvbase)的偏移量其单位是字节。由于正值比负值便于表达所以使用正值表示偏移量。为此就须将最上层的基类(即classvbase)的数据成员(objgvb)放置于内存栈区空间的较后(也可能最后)单元内。这是和无虚基类情况不同之处(如无虚基类则将最上层的基类的数据成员放置于内存栈区空间的较前(通常为第一个)单元内)。在具有虚基类的情况下对象的内存栈区空间分为两部分:前半部分中按顺序存放着虚基类的直接派生类部分及其以下所有派生类部分的非静态数据成员(以上程序中按顺序为objgdx、objgdy和objggr)而后半部分中则按顺序存放着虚基类的间接基类部分和直接基类部分以及虚基类本身部分的非静态数据成员(以上程序中为objgvb)。具体而言classmultigrand的对象objg的栈区存储内容的前半部分包括五个单元。其中第一单元(指针)和第二单元(classderx部分的数据值objgdx)组合地表示该数据成员objgdx的数值以及它与最上层的基类(以上程序中此为classvbase)部分的数据成员(即classvbase部分的数据objgvb)之间的距离偏移量(程序中为h意味着十进制即该指针与objgvb相距个单元)。对象objg的栈区存储内容的前半部分中的第三单元(指针)和第四单元(classdery部分的数据值objgdy)组合地表示该数据成员objgdy的数值以及它与最上层的基类classvbase部分的数据成员(即classvbase部分的数据objgvb)之间的距离偏移量(程序中为ch即该指针与objgvb相距个单元)。对象objg的栈区存储内容的前半部分中的第五单元是classmultigrand部分的数据objggr。对象objg的栈区存储内容的后半部分中只有一个单元(即第六单元)它是最上层的基类(即classvbase)的数据成员objgvb。为何以上程序中需要两个指针?这是考虑到虚基类具有两个直接派生类。当建立这两个直接派生类的对象时就将分别使用其中的一个。下面看一下建立其中一个直接派生类classderx的对象objx的例子。例建立classderx的对象objxcontvclscppInitializationofargumentofvirtualbaseofapartoftwolevelsofderivedclasses#include<iostreamh>classvbase{public:vbase(intj){vb=j}intvb}classderx:virtualpublicvbase{public:derx(inti,intj):vbase(i){dx=j}intdx}classdery:virtualpublicvbase{public:dery(inti,intj):vbase(j){dy=j}intdy}classmultigrand:publicderx,publicdery{public:multigrand(inta,intb,intc,intd,inte,intf):derx(a,b),dery(c,d),vbase(f){gr=c}intgr}voidmain(){derxobjx(,)cout<<"sizeof(objx):"<<sizeof(objx)<<endlint**ptr=(int**)objx指向派生类对象objxcout<<hex<<"Addrofobject:"<<ptr<<endlcout<<"Contentsofobj:"<<endlcout<<"Firstelement:"<<*ptr<<endlcout<<"Indexingoffirstelement:"<<**ptr<<endlcout<<"secondindexingoffirstelement:"<<*(*ptr)<<'h'<<endlptrcout<<"Secondelement:"<<(int)*ptr<<endlptrcout<<"Thirdelement:"<<(int)*ptr<<endlcout<<"Valuesofdata:"<<endlcout<<"objxvb="<<objxvb<<endlcout<<"objxdx="<<objxdx<<endl}*Results:sizeof(objx):Addrofobject:xFDECContentsofobj:Firstelement:xCCIndexingoffirstelement:secondindexingoffirstelement:hSecondelement:Thirdelement:Valuesofdata:objxvb=objxdx=*DAGpublicpublicclassderx的对象objx的双区存储内容栈区数据区CC(指针)(objxdx)(objxvb)classderx的对象objx的栈区存储内容的前半部分包括两个单元即第一单元(指针)和第二单元(classderx部分的数据值objxdx)它们组合地表示该数据成员objxdx的数值以及它与最上层的基类(以上程序中此为classvbase)部分的数据成员(即classvbase部分的数据objxvb)之间的距离偏移量(程序中为h意味着该指针与objxvb相距个单元)。对象objg的栈区存储内容的后半部分中只有一个单元(即第三单元)它是最上层的基类(即classvbase)的数据成员objxvb。以上是最简单的同时使用虚基类和多继承的例子。下面看一个稍微复杂的例子。例与上例不同多了两个类:最上层的基类不是虚基类而是虚基类classvbase的直接基类classtop多继承派生类classmultigrand下面还有一个直接派生类classbottom。contvclscppInitializationofargumentofvirtualbaseoffivelevelsofderivedclasses#include<iostreamh>classtop{public:top(inti){tp=i}inttp}classvbase:publictop{public:vbase(inti,intj):top(i){vb=j}intvb}classderx:virtualpublicvbase{public:derx(inti,intj):vbase(i,j){dx=j}intdx}classdery:virtualpublicvbase{public:dery(inti,intj):vbase(i,j){dy=j}intdy}classgrand:publicderx,publicdery{public:grand(inta,intb,intc,intd,inte,intf):derx(a,b),dery(c,d),vbase(e,f){gr=c}intgr}classbottom:publicgrand{public:bottom(inta,intb,intc,intd,inte,intf,intg):grand(a,b,c,d,e,f),vbase(e,f){bot=a}intbot}voidmain(){bottomobjb(,,,,,,)cout<<"sizeof(objb):"<<sizeof(objb)<<endlint**ptr=(int**)objb指向基类对象bscout<<hex<<"Addrofobjectbs:"<<ptr<<endlcout<<"Contentsofobj:"<<endlcout<<"Firstelement:"<<*ptr<<endlcout<<"Indexingoffirstelement:"<<**ptr<<endlcout<<"secondindexingoffirstelement:"<<*(*ptr)<<'h'<<endlptrcout<<"Secondelement:"<<(int)*ptr<<endlptrcout<<"Thirdelement:"<<*ptr<<endlcout<<"Indexingofthirdelement:"<<**ptr<<endlcout<<"secondindexingofthirdelement:"<<*(*ptr)<<'h'<<endlptrcout<<"Fourthelement:"<<(int)*ptr<<dec<<endlptrcout<<"Fifthelement:"<<(int)*ptr<<endlptrcout<<"Sixthelement:"<<(int)*ptr<<endlptrcout<<"Seventhelement:"<<(int)*ptr<<endlptrcout<<"Eighthelement:"<<(int)*ptr<<endlcout<<"Valuesofdata:"<<endlcout<<"objbtp="<<objbtp<<endlcout<<"objbvb="<<objbvb<<endlcout<<"objbdx="<<objbdx<<endlcout<<"objbdy="<<objbdy<<endlcout<<"objbgr="<<objbgr<<endlcout<<"objbbot="<<objbbot<<endl}*Results:sizeof(objb):Addrofobjectbs:xFDDContentsofobj:Firstelement:xBIndexingoffirstelement:secondindexingoffirstelement:hSecondelement:Thirdelement:xCIndexingofthirdelement:secondindexingofthirdelement:hFourthelement:Fifthelement:Sixthelement:Seventhelement:Eighthelement:Valuesofdata:objbtp=objbvb=objbdx=objbdy=objbgr=objbbot=*DAGclassbottom的对象objb的双区存储内容栈区数据区B(指针)(objbdx)C(指针)(objbdy)(objbgr)(objbbot)(objbtp)(objbvb)以上程序中classbottom的对象objb的栈区存储内容的前半部分包括六个单元。其中第一单元(指针)和第二单元(classderx部分的数据值objbdx)组合地表示该数据成员objbdx的数值以及它与最上层的基类(以上程序中此为classtop)部分的数据成员(即classtop部分的数据objbtp)之间的距离偏移量(程序中为h意味着该指针与objbyp相距个单元)。对象objb的栈区存储内容的前半部分中的第三单元(指针)和第四单元(classdery部分的数据值objbdy)组合地表示该数据成员objbdy的数值以及它与最上层的基类classtop部分的数据成员(即classtop部分的数据objbtp)之间的距离偏移量(程序中为h即该指针与objgvb相距个单元)。对象objb的栈区存储内容的前半部分中的第五和第六单元分别是classmultigrand部分的数据objbgr和classbottom部分的数据objbbot。对象objb的栈区存储内容的后半部分中有两个单元按顺序看它们分别是最上层的基类(即classtop虚基类classvbase的直接基类)部分的数据成员objbtp和虚基类classvbase部分的数据成员objbvb。为了研究程序中对象的栈区存储内容的两个指针中的另外一个下面看一下建立另一个直接派生类classdery的对象objy的例子。例classdery的对象objycontvclscppInitializationofargumentofvirtualbaseofapartofthreelevelsofderivedclasses#include<iostreamh>classtop{public:top(inti){tp=i}inttp}classvbase:publictop{public:vbase(inti,intj):top(i){vb=j}intvb}classderx:virtualpublicvbase{public:derx(inti,intj):vbase(i,j){dx=j}intdx}classdery:virtualpublicvbase{public:dery(inti,intj):vbase(i,j){dy=j}intdy}classgrand:publicderx,publicdery{public:grand(inta,intb,intc,intd,inte,intf):derx(a,b),dery(c,d),vbase(e,f){gr=c}intgr}classbottom:publicgrand{public:bottom(inta,intb,intc,intd,inte,intf,intg):grand(a,b,c,d,e,f),vbase(e,f){bot=a}intbot}voidmain(){deryobjy(,)cout<<"sizeof(objy):"<<sizeof(objy)<<endlint**ptr=(int**)objy指向基类对象objycout<<hex<<"Addrofobject:"<<ptr<<endlcout<<"Contentsofobj:"<<endlcout<<"Firstelement:"<<*ptr<<endlcout<<"Indexingoffirstelement:"<<**ptr<<endlcout<<"secondindexingoffirstelement:"<<*(*ptr)<<'h'<<endlptrcout<<"Secondelement:"<<(int)*ptr<<endlptrcout<<"Thirdelement:"<<(int)*ptr<<endlptrcout<<"Fourthelement:"<<(int)*ptr<<dec<<endlcout<<"Valuesofdata:"<<endlcout<<"objytp="<<objytp<<endlcout<<"objyvb="<<objyvb<<endlcout<<"objydy="<<objydy<<endl}*Results:sizeof(objy):Addrofobject:xFDEContentsofobj:Firstelement:xCIndexingoffirstelement:secondindexingoffirstelement:hSecondelement:Thirdelement:Fourthelement:Valuesofdata:objytp=objyvb=objydy=*DAGclassdery的对象objy的双区存储内容栈区数据区C(指针)(objydy)(objytp)(objyvb)以上程序中classdery的对象objy的栈区存储内容的前半部分包括两个单元。其中第一单元(指针)和第二单元(classdery部分的数据值objydy)组合地表示该数据成员objydy的数值以及它与最上层的基类(以上程序中此为classtop)部分的数据成员(即classtop部分的数据objytp)之间的距离偏移量(程序中为h意味着该指针与objytp相距个单元)。对象objy的栈区存储内容的后半部分中有两个单元(即对象objy的栈区存储内容中的第三和第四单元)它们分别是最上层的基类(即classtop)部分的数据成员objytp和classvbase部分的数据成员objyvb。使用继承的总结性例子共有以下六个文件:personhpersoncppadulthadultcppretirehretirecpp以上各组独立运行一次只运行一组。各类之间的关系为:它的DAG见personppt即DAGpublicpublicpersonh#if!defined(PERSONH)#definePERSONH#include<iostreamh>#include<stringh>classperson{protected:char*nameshortagecharsexpublic:person(char*nm,shortag,charsx){name=newcharstrlen(nm)strcpy(name,nm)age=agsex=sx}voiddisplay()const{cout<<"namet:"<<name<<endlcout<<"aget:"<<age<<endlcout<<"sext:"<<sex<<endl}~person(){deletename}}#endifPERSONHpersoncpp#include"personh"voidmain(){personW("Wang",,'M')Wdisplay()}*Results:name:Wangage:sex:M*现加一项:intsalary如下。adulth增加的数据用蓝字标出#if!defined(ADULTH)#defineADULTH#include"personh"classadult:publicperson{protected:intsalarypublic:adult(char*nm,shortag,charsx,intsal)voiddisplay()const}adult::adult(char*nm,shortag,charsx,intsal):person(nm,ag,sx){salary=sal}voidadult::display()const{person::display()cout<<"salaryt:"<<salary<<endl}#endifADULTHadultcpp#include"adulth"voidmain(){adultC("Chen",,'F',)Cdisplay()}*Results:name:Chenage:sex:Fsalary:*又加一项:charretired如下。retireh增加的数据用蓝字标出#if!defined(RETIREH)#defineRETIREH#include"adulth"classretire:publicadult{protected:charretiredpublic:retire(char*nm,shortag,charsx,intsal,charret)voiddisplay()const}retire::retire(char*nm,shortag,charsx,intsal,charret):adult(nm,ag,sx,sal){retired=ret}voidretire::display()const{adult::display()cout<<"retiredt:"<<retired<<endl}#endifRETIREHretirecpp#include"retireh"voidmain(){retireZ("Zu",,'M',,'Y')Zdisplay()}*Results:name:Zuage:sex:Msalary:retired:Y*(第四章完)grandobjyk=derivebasestringderivedstringxxgrx=derx=grderive::x=px=derbase::x=grbase::x=retireadultperson第三组第二组第一组W::W(a,b,c,d,e,f):X(a,b),Y(c,d),V(e)z(i,j)y(i,j)x(i,j)grx=derx=grderive::x=Z::Z(a,b,c,d,e,f,g):W(b,c,d,e,f,g),V(f,g)W(a,b,c,d,e,f):X(a,b),Y(c,d),V(e,f)X::X(i,j):V(i,j)Y::Y(i,j):V(i,j)V::V(i,j):B(i)B::B(i)X::X(i,j):V(i)Y::Y(i,j):V(i)V::V(i)D::D(cc,cb,i,dd,str):B(cb,i),C(cc,i),m(dd),A(i)C::C(chc,i)B::B(chb,i)A::A(i)grandderivederivevbasebasevbasederypx=grbase::x=multigrandderx=vbaseC::C(chc,i)A::A(i)B::B(chb,i)A::A(i)derbase::x=px=grandgrandderivederivebasebaseDAAderivederivebasehzxyCBAD::D(a,b,c,d,e):C(b,c,d),member(e)C::C(b,c,d):B(b,c)B::B(b,c):A(c)A::A(c)BACBAzyobjzy::k=objyx::j=objzx::j=objyx::i=objzx::i=objxj=objxi=z(i,j)y(i,j)yx(i,j)zderbase::x=px=grbase::x=几何图形几何图形三角形四边形矩形平行四边形正方形圆形grderive::x=derxgrx=chvbasederxderyderxhvbasederymultigrandhhtopbottomtopvbasederxdery

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/71

C++课件--薛景瑄chapter_4

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利