程序的类层次结构课件_第1页
程序的类层次结构课件_第2页
程序的类层次结构课件_第3页
程序的类层次结构课件_第4页
程序的类层次结构课件_第5页
已阅读5页,还剩69页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

程式的類層次結構

程式的類層次結構類的繼承與派生類層次中成員函數名的多態性派生面向對象的程式設計的一個重要特點是允許以既有類(也稱基類),以其為基礎導出(定義)新的類(也稱派生類)。這一過程稱為派生派生時不需要把既有類的相關代碼重新書寫一遍,只需要指明是以哪個類為基類,便可以將基類中的有關特徵繼承過來,實現了部分代碼的可重用。類的繼承與派生派生方式派生類的構造函數與釋放函數多基派生虛基類類層次中成員名的作用域類層次中的類轉換派生方式public派生與private派生Protected成員與protected派生public派生與private派生C++允許程式員用下邊的格式用一個類派生它的子類:

class派生類名

:派生方式

基類名

{

private:

新增私有成員聲明語句表列

public:

新增公開成員聲明語句表列

};兩種派生方式的特點1)無論哪種派生方式,基類中的private成員在派生類中都是不可見的。也就是說,基底類中的private成員不允許外部函數或派生類中的任何成員訪問。2)public派生時,基類中的public成員相當於派生類中的public成員。3)private派生時,基類中的public成員相當於派生類中的private成員派生後基類成員訪問性的變化基類成員的訪問性privatepublic派生方式privatepublicprivatepublic派生类成員不可見可見可見外部函数不可見可見定義Location—Point類層次結構

classLocation{ //基類介面定義public:

int

mX_Pos,mY_Pos; //位置的座標,以像素點計

Location(intx,inty); //構造函數,初始化位置座標

int

getX(); //返回當前位置的x座標

int

getY(); //返回當前位置的y座標

};

enumBOOLEAN{ FALSE,TRUE}; //定義一個布爾類型

classPoint:privateLocation{ //派生類介面定義

BOOLEANmVisible; //可見性變數

Public:

Point(intx,inty); //構造函數,初始化點的位置

BOOLEAN isVisible();//返回當前點是否可見

Void show(); //顯示當前點

Void hide(); //隱藏當前點

Void moveTo(); //移動當前點

};Protected成員與protected派生protected成員是一種血緣關係內外有別的成員。它只為它所在類中的方法和由它直接派生的類方法可見private、protected,、public作為類成員的可見性修飾符,將產生如下影響:

1)在一個類中定義的方法函數,可以訪問本類中的任何成員,但只能訪問基類中的protected成員和public成員;

2)一個類對象,只能使用本類或其public派生基類中的public成員例5.1.2classb{protected:

intx1;public:

intx2; b(){x1=x2=5;}

};classd1:publicb{public:

intfd11(){ returnx1; //ok,可以訪問基類protected成員

returnx2; //ok,可以訪問基類public成員

}

intfd12(){ bbb; //returnbb.x1;類對象不可使用本類protected成員

returnbb.x2; //ok,類對象可以使用基類public成員

}};定義Location—Point—Circle類層次結構

classLocation //位置類{ protected: //保護類成員

int

mX_Pos,mY_Pos; //在Location派生類中可訪問public:Location(intx,inty);

int

getX();

int

getY(); };

enumBOOLEAN{ FALSE,TRUE}; classPoint:publicLocation //點類{ protected: //保護類成員

BOOLEANmVisible; //在Point派生類中可訪問Public:

Point(intx,inty); BOOLEAN isVisible(); Void show(); Void hide(); Void moveTo(); };classCircle:publicPoint //圓類{protected: //保護類成員

int

mRadius; //圓半徑,在Circle派生類中可訪問Public:

Circle(intx,inty,intr); //初始化圓

void show(); //畫圓

void hide(); //隱藏圓

void moveTo(); //移動圓

void expand(intdelta); //放大圓,半徑為(r+delta)

void contract(intdelta); //縮小圓,半徑為(r-delta)

};派生類中訪問屬性發生如下變化1)基類的private成員在派生類中是不可見的2)private派生使基類中的非private成員都成為派生類中的私有成員;protected派生使基類的非private成員在派生類中都變為protected成員;public派生使基類的非private成員在派生類中訪問屬性保持不變3)不同的派生方式引起成員訪問屬性的改變,只能降低,不能提高(如將private成員提升為protected成員或public成員)。但是,C++允許在派生類中用訪問屬性修飾符,恢復一個成員原來的訪問屬性例5.1.4classb{protected:

intx1;

intx2;public:

intx3;

intx4;…

};classd:privateb //x1,x2,x3,x4將派生為private成員{ protected:b::x1; //將x1恢復為protected成員

public:b::x3; //將x3恢復為public成員

…};派生類的構造函數與釋放函數類層次結構中數據成員的存儲派生類中構造函數/釋放函數的定義與調用派生類對象的創建類層次結構中數據成員的存儲Location—Point—Circle類層次結構中數據成員的存儲mX_PosmY_PosmX_PosmY_PosmVisiblemX_PosmY_PosmVisiblemRadius構造函數/釋放函數的定義與調用首先通過派生類的構造函數調用基類的構造函數,對基類成員進行初始化然後再執行對在派生類中新增的成員進行初始化的操作因此可以說,派生類對象是由其所有先輩類共同創建的派生類的構造函數原型格式

Y::Y(argX1,argX2,…,argY1,argY2,…):X(argX1,argX2,…)其中,X為Y的直接基類名,Y為派生類名。這一格式是遞歸的類層次結構Location—Point—Circle中的構造函數Location::Location(intx,inty){

mX_Pos=x;mY_Pos=y;}

Point::Point(intx,inty):Location(x,y)//先調用基類構造函數{ visible=FALSE; //缺省情況下是不可見的}Circle::Circle(intx,inty,intr):Point(x,y)//先調用基類構造函數{

mRadius=r;}說明1)創建一個Circle類對象時,要自動調用Point類的構造函數,再由Point類的構造函數調用其父類Location構造函數2)當一個派生類對象撤銷時,釋放函數調用的順序與構造函數相反3)保護成員具有良好的繼承性,對保護成員的初始化最好在基類中進行4)派生類的構造函數的構成形式,與包含對象成員的類的構造函數相似但不相同派生類對象的創建派生類對象可以用兩種方式創建:

1)用常數參數表創建

2)由部分基類對象創建派生類對象多基派生派生類只有一個基類時,稱為單基派生。一個派生類具有多個基類時,稱為多基派生或多重繼承(multipleinheritance),這時將繼承每個基類的部分代碼。多基派生是單基派生的擴展。與單基派生相比,既有同一性,又有特殊性。單基派生則可以看成多基派生的特例例由Hard(機器名)類與Soft(軟體,由os與Language組成)類派生出System類

#include<iostream.h>#include<string.h>classHard{protected:charbodyname[20];public:Hard(char*bdnm)//構造函數

{cout<<"conH\n";

strcpy(bodyname,bdnm); }

Hard(Hard&abody)//複製構造函數

{cout<<"copyH\n";

strcpy(bodyname,abody.bodyname); } voidprint(){cout<<"Body_name:"<<bodyname<<endl; }};

classSoft{protected:

charos[10];charlang[15];public:Soft(char*o,char*lg) //構造函數

{cout<<"conF\n";strcpy(os,o);strcpy(lang,lg);}Soft(Soft&asoft) //複製構造函數

{

cout<<"copyF\n";

strcpy(os,asoft.os);

strcpy(lang,asoft.lang);}voidprint(){cout<<"os:"<<os<<",language:"<<lang<<endl;}};classSystem:publicHard,publicSoft //派生類System{ charowner[10];public:System(char*ow,char*bn,char*o,char*lg):Hard(bn),Soft(o,lg) //調用基類構造函數

{cout<<"conS\n";strcpy(owner,ow);}System(Hardabody,Softasoft,char*ow):Hard(abody),Soft(asoft) //調用基類複製構造函數

{cout<<"copyS\n";strcpy(owner,ow);}voidprint(){cout<<"owner:"<<owner;

cout<<";\nhard:"<<bodyname;

cout<<";\nsoft:"<<os<<","<<lang<<endl;}};測試程式

voidmain(){Systembsystem("Wang",//用常參數表創建派生類對象

"IBMPC","PCDOS",

"TrueBASIC");

bsystem.print();

cout<<"Ok!\n";[y1]

Hardabody("AST386sx/16");Softasoft("PC

DOS","BorlandC++");Systemasystem("AST386sx/16",//基類對象創建派生類對象

asoft,"Zhang");

asystem.print();}虛基類在多基派生中,如果在多條繼承路徑上有一個公共的基類(如圖5.5(a)中的base0),則在這些路徑的匯合點(如圖5.5(a)中的derived類對象),便會產生來自不同路徑的公共基類的多個拷貝,如圖5.5(b)所示。如果只想保留公共基類的一個拷貝,就必須使用關鍵字virtual把這個公共基類定義為虛基類在多條繼承路徑上公共基類的情形base0base1base2derivedbase0{a}base0{a}base1base2derived在定義基類直接派生的類時說明格式class派生類名

:virtual派生方式

基類名

{

…

};例5.1.7classbase0{public:

inta;…};classbase1:virtualpublicbase0{…};classbase2:virtualpublicbase0{…};classderived:publicbase1,publicbase2{…};幾點說明1)virtual也是“派生方式”中的一個關鍵字,它與訪問控制關鍵字(public或private,protected)間的先後順序無關緊要2)為了保證虛基類在派生類中只繼承一次,就必須將其直接派生類定義時都說明為虛擬派生;否則除從用作虛基類的所有路徑中得到一個拷貝外,還從其他作為非虛基類的路徑中各得到一個拷貝3)虛基類對象的初始化圖5.6a所示的類層次結構,當A與B都是C與D的虛基類時,系統將要自左向右按深度優先遍曆演算法對公共派生類E進行初始化虛基類對象的初始化CBADEABABCDE類層次中成員名的作用域private成員的作用域只在本類對象內部public成員和protected成員,在類層次結構中將形成成員名多重作用域:

·基類成員作用域和派生類新增成員作用域形成相包含的關係,派生類新增成員作用域為內層,基類成員作用域在外層

·內層聲明的識別字可以覆蓋外層聲明的同名識別字,使其不可見;對於多基派生類,如果其多個基類中擁有同名成員,而在派生類中又新增了同名成員,則派生類的該同名成員將覆蓋所有基類的同名成員

·如果在幾個基類中具有同名成員,但在派生類中沒有與它們同名的新增成員名,則由於這些成員名具有相同的作用域,使系統無法判定到底是調用哪個基類成員有類層次結構:A←B←C#include<iostream.h>classA{public:

int

x,a;voidfun(inti){x=i;

cout<<"A::x="<<x<<endl;

cout<<endl;}};classB:publicA{public:

int

x,b;voidfun(inti){x=i*a; //引用非同名基類成員

cout<<"B::x="<<x<<endl; //引用本類成員

cout<<"A::x="<<A::x<<endl; //引用同名基成員

cout<<"a="<<a<<endl; //引用非同名基成員

cout<<endl;}};classC:publicB{public:

intx;voidfun(inti) { x=i*a*b; //引用非同名基類變數

cout<<“C::x=“<<x<<endl; //引用本類成員

cout<<“A::x=“<<A::x<<“,B::x=“<<B::x<<endl;//引用同名基類成員

cout<<“a=“<<a<<“,b=“<<b<<endl; //引用非同名基類成員

cout<<endl;}};

測試程式

voidmain(){

inti=2;Cobj;

obj.a=1; //引用非同名基成員

obj.A::fun(i); //引用同名基成員

obj.b=2; //引用非同名基成員

obj.B::fun(i); //引用同名基成員

obj.fun(i); //引用派生類成員

}例5.1.9classBase{public:staticvoidsb(); //靜態成員

voidfb();};classD:privateBase{}; //全私有派生classDD:publicD{voidfdd();};

voidDD::fdd(){Base::sb();//對

//fb();錯

//sb();錯

}類層次中的類轉換單基派生的情形多繼承的情形含有公共虛基類的類層次結構單基派生的情形

#include<iostream.h>#include<string.h>classPerson{ char*mName; charmSex;

int

mAge;public: Person(char*n="",inta=50,chars='m') { mName=newchar[strlen(n)+1];

strcpy(mName,n);

mAge=a;

mSex=s; } ~Person() { delete[]mName; } void show() { cout<<“\nname:”<<mName;

cout<<“-age:”<<mAge;

cout<<“-sex:”<<mSex<<endl; } Person&operator=(Person&p) { delete[]mName;

mName=newchar[strlen(p.mName)+1];

strcpy(mName,p.mName);

mAge=p.mAge;

mSex=p.mSex; return*this; }};classEmployee:publicPerson{char*mDepartment;floatmSalary;public:Employee(char*,int,char,char*,float);voidshow();~Employee() {

delete[]mDepartment; }};Employee::Employee(char*nm="",int

ag=0,charsx='', char*dprt="",floatslr=0)

:Person(nm,ag,sx)

{

mDepartment=newchar[strlen(dprt)+1];

strcpy(mDepartment,dprt);

mSalary=sly;}

voidEmployee::show(){Person::print();

cout<<"-department:"<<department;

cout<<"-salary:"<<salary<<endl;}測試程式

voidmain(){Employeezh("Zhang",50,'m',"SHXIEMU",180);Personc,*pP;c=zh; //1用派生類對象給基類對象賦值

c.show();Person&y=zh; //2用派生類對象初始化基類的引用

y.show();

pP=&zh; //3把派生類對象地址賦值給基類指針

pP->show();((Employee*)pP)->show();//4基類指針向派生類指針強制轉換

}多繼承的情形classbase0{protected:

intb0;};classbase1:publicbase0{protected:

intb1;};classbase2:publicbase0{protected:

intb2;};classderived:publicbase1,publicbase2{floatd;public:derived();};各層次數據成員在內存中的分佈Pb1intb0intb1intb0intb2…base0base0base1base2derivedPb2base0base1base2derived克服對基類成員訪問的二義性對指針要顯式地指明全路徑將指針先強制轉換到不會產生二義性的基類顯式指明成員來自哪個類含有公共虛基類的類層次結構使用虛基類,在它的幾條派生路經的匯合處,只產生其一個拷貝引用的都是同一個虛基類中的數據成員具有相同的值。可以進行如下的類變換

1)派生類對象的地址可以直接賦給間接公基類的指針,並且不需進行強制類型轉換

2)一個虛基類的引用,可以引用一個派生類的對象類層次中成員函數名的多態性虛函數與動態綁定虛函數的訪問純虛函數與抽象類虛釋放函數多基派生中虛函數的二義性虛函數與動態綁定虛函數是用關鍵字virtual修飾的某基類中的protected或public成員函數。它可以在派生類中重定義,以形成不同的版本。只有在程式執行過程中,依據指針具體指向哪個類對象,或依據引用哪個類對象,才能確定啟動哪一個版本,實現動態綁定。即這種綁定方式不是在編譯時靜態地進行,而必須在程式運行過程中進行動態綁定C++中靜態多態性與動態多態性的主要特徵形式綁定方式各版本的函數原型綁定依據特點函數名重載靜態綁定不同參數數目及類型高效虛函数動態綁定相同程式運行時引用或指針指向高靈活性、抽象性和可擴充性點─圓類層次結構不使用虛函數

#include<iostream.h>classPoint //基類{

private:

floatmX,mY;

public:

Point(){}

Point(float

i,floatj)

{

mX=i;mY=j;

}

floatarea() //非虛函數

{

return0.0;

}

};constfloatPi=3.141593;classCircle:publicPoint //派生類{

private:

floatmRadius;

public:

Circle(floatr)

{

mRadius=r;

}

floatarea()

{

returnPi*mRadius*mRadius;

}

};點─圓類層次結構使用虛函數#include<iostream.h>

classPoint{

private:

floatmX,mY;

public:

Point(){}

Point(floati,floatj)

{

mX=i;mY=j;

}

virtualfloatarea() //聲明為虛函數

{

return0.0;

}

};constfloatPi=3.141593;classCircle:publicPoint{

private:

floatmRadius;

public:

Circle(floatr)

{

mRadius=r;

}

floatarea() //虛函數的再定義

{returnPi*mRadius*mRadius;

}

};虛函數聲明與重定義的一般規則1)在基類中,用關鍵字virtual可以將其public或protected部分的成員函數聲明為虛函數2)一個虛函數是屬於它所在的類層次結構的,而不是只屬於某一個類,只不過它在該類層次結構中的不同類中具有不同的形態3)如果派生類中沒有對基類中說明的虛函數進行重定義,則它繼承基類中的虛函數4)由於有些編譯器不能正確處理虛函數,因此要避免把構造函數聲明為虛函數5)虛函數只能是類的成員函數,不能把虛函數聲明為靜態的或全局的;也不能把友元說明為虛函數。但是虛函數可以是另一個類的友元函數虛函數的訪問用基指針訪問與用對象名訪問訪問該類層次中的虛函數——要使用this指針用構造函數/釋放函數訪問用基指針訪問與用對象名訪問把一個函數聲明為虛函數,保證了這個函數被指向基類的指針(或引用)調用時,C++系統對其進行動態綁定,向實際的對象傳遞消息但是,通過一個對象名訪問虛函數時,C++系統將使用靜態綁定例5.2.2

intmain()

{

Circlec(6.4321);

Cout<<c.area()<<endl;

Cout<<c.Point::area()<<endl;

Cout<<c.Circle::area()<<endl;

}這時,可以用作用域運算符指定使用的是哪個類的虛函數。這與非虛函數沒有區別。這裏所說的“指向基類”是指指向最先將一個成員函數聲明為虛函數的類。例5.2.3

#include<iostream.h>classbase0{public:voidv(){cout<<"base0\n";} //非虛函數

};classbase1:publicbase0{public:virtualvoid

v(){cout<<"base1\n";} //聲明虛函數};classA1:publicbase1{public:voidv(){cout<<"A1\n";}};classA2:publicA1{public:voidv(){cout<<"A2\n";}};classB1:privatebase1{public:voidv(){cout<<"B1\n";}};classB2:publicB1{public:voidv(){cout<<"B2\n";}};訪問該類層次中的虛函數#include<iostream.h>classA{public:virtualvoidv1(){cout<<"v1iscalledinA.\n";a1();}voida1(){cout<<"a1iscalledinA.\n";v2();}virtualvoidv2(){cout<<"v2iscalledinA.\n";}};classB:publicA{public:void

b1(){cout<<"b1iscalledinB.\n";v2();}virtualvoidv2(){cout<<"v2iscalledinB.\n";}};這裏,this是一個指向基類A的指針。由於v2()是一個虛函數,因此C++系統要通過動態綁定實現,具體取決於所引用的對象用構造函數/釋放函數訪問#include<iostream.h>classbase{public:base(){};virtualvoidv1(){cout<<

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论