《C++面向?qū)ο蟪绦蛟O(shè)計》課件第5章_第1頁
《C++面向?qū)ο蟪绦蛟O(shè)計》課件第5章_第2頁
《C++面向?qū)ο蟪绦蛟O(shè)計》課件第5章_第3頁
《C++面向?qū)ο蟪绦蛟O(shè)計》課件第5章_第4頁
《C++面向?qū)ο蟪绦蛟O(shè)計》課件第5章_第5頁
已閱讀5頁,還剩232頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

5.1繼承與派生(InheritanceandDerivation)

5.2派生類的訪問控制(DerivedClassesAccessControl)5.3派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)

(DerivedClassesConstructorsandDestructors)

5.4多繼承(Multi-Inheritance)

5.5虛基類(VirtualBaseClasses)

5.6賦值兼容規(guī)則(CompatibleAssignmentRules)5.7程序舉例(ProgramExamples)

5.8常見編程錯誤(CommonProgrammingErrors)本章小結(jié)(ChapterSummary)習題5(Exercises5)

5.1.1繼承的概念(InheritanceConcept)

1.繼承的含義

在自然界中,猴子是靈長目的一大類,金絲猴具有猴子的共有特征,同時又有不同于其他猴子的漂亮的金色猴毛;現(xiàn)實生活中,子女的外貌、血型往往不是僅僅繼承自父親或母親,而是將父母親的特點都繼承下來。5.1繼?承?與?派?生(InheritanceandDerivation)

2.繼承的種類

每一個派生類都有且僅有一個基類,派生類可以看作是基類的特例,它增加了某些基類所沒有的性質(zhì)。這種繼承方式稱為單繼承或單向繼承。派生類又作為基類繼續(xù)派生新的類,這樣的派生方式稱為多層派生,從繼承的角度稱為多繼承,與之相類似,如果一個派生類有兩個或兩個以上的基類,則稱為多繼承或多重繼承。如圖5-1所示。圖5-1單繼承和多繼承

3.繼承機制的特點

通過繼承機制,可以利用已有的數(shù)據(jù)類型來定義新的數(shù)據(jù)類型,所定義的新的數(shù)據(jù)類型不僅擁有新定義的成員,而且同時還擁有舊的成員。

C++的繼承關(guān)系有以下幾個特點:

(1)一個派生類可以有一個或多個基類,只有一個基類時,稱為單繼承;有多個基類時,稱為多繼承。

(2)繼承關(guān)系可以是多級的,即類Y繼承類X和類Z繼承類Y可以同時存在。

(3)不允許繼承循環(huán),例如,不能有類Y繼承類X、類Z繼承類Y和類X繼承類Z同時存在。

(4)基類中能夠被繼承的部分只能是公有成員和保護成員(具體概念將在后面介紹),私有成員不能被繼承。5.1.2派生類的聲明(DeclarationofDerivedClasses)

1.單繼承的定義

單繼承的定義格式如下:

class<派生類名>:<繼承方式><基類名>

{

<派生類新定義成員>;

};【例5-1】在普通的時鐘類Clock基礎(chǔ)上派生出鬧鐘類AlarmClock。

程序如下:

#include<iostream>

usingnamespacestd;

classClock

{

private:

intH,M,S;

public:?voidSetTime(intH=0,intM=0,intS=0);

voidShowTime();

Clock(intH=0,intM=0,intS=0);

~Clock();

};

classAlarmClock:publicClock

{

private:intAH,AM; //響鈴的時間

boolOpenAlarm; //是否關(guān)閉鬧鐘

public:

SetAlarm(intAH,intAM); //設(shè)置響鈴時間

SwitchAlarm(boolOpen=true); //打開/關(guān)閉鬧鈴

ShowTime(); //顯示當前時間與鬧鈴時間

}

派生類AlarmClock的成員構(gòu)成圖如圖5-2所示。圖5-2派生類AlarmClock的成員構(gòu)成圖

2.派生類的實現(xiàn)方式

1)吸收基類成員

基類的全部成員被派生類繼承,作為派生類成員的一部分。

2)改造基類成員

派生類根據(jù)實際情況對繼承自基類的某些成員進行限制和改造。

3)添加新成員

派生類在繼承基類成員的基礎(chǔ)之上,根據(jù)派生類的實際需要,增加一些新的數(shù)據(jù)成員和函數(shù)成員,以描述某些新的屬性和行為。

3.多繼承的定義

多繼承可以看做是單繼承的擴充,它是指由多個基類派生出一個類的情形。

多繼承的定義格式如下:

class派生類名:繼承方式1基類名1,繼承方式2基類名2,…

{

private:

派生類的私有數(shù)據(jù)和函數(shù)

public:

派生類的公有數(shù)據(jù)和函數(shù)

protected:

派生類的保護數(shù)據(jù)和函數(shù)

};

4.基類與派生類的關(guān)系

任何一個類都可以派生出一個新類,派生類也可以再派生出新類,因此基類和派生類是相對而言的。一個基類可以是另一個基類的派生類,這樣便形成了復(fù)雜的繼承結(jié)構(gòu),出現(xiàn)了類的層次。一個基類派生出一個派生類,它又變成另一個派生類的基類,則原來基類為該派生類的間接基類,其關(guān)系如圖5-3所示,其中,類A是類C的間接基類,而類B是類A的直接派生類。圖5-3類的層次

1)派生類是基類的具體化

類的層次通常反映了客觀世界中某種真實的模型。例如,定義輸入設(shè)備為基類,而鍵盤和鼠標將是派生類,它們的關(guān)系圖如圖5-4所示。在這種情況下不難看出,基類是對若干個派生類的抽象,而派生類是基類的具體化?;惓槿×怂呐缮惖墓刑卣?,而派生類通過增加行為將抽象類變?yōu)槟撤N有用的類型。圖5-4繼承的實例

2)派生類是基類定義的延續(xù)

先定義一個抽象基類,該基類中有些操作并未實現(xiàn),然后定義非抽象的派生類,實現(xiàn)抽象中基類中定義的操作。

3)派生類是基類的組合

在多繼承時,一個派生類有多于一個的基類,這時派生類將是所有基類行為的組合。5.2.1公有繼承(PublicInheritance)

若在定義派生類時,繼承方式為public,則定義公有派生。

5.2派生類的訪問控制(DerivedClassesAccessControl)

【例5-2】

用學生檔案類Student派生學生成績類Score。討論基類中公有、私有與保護數(shù)據(jù)成員在派生類中的訪問權(quán)限。

程序如下:

#include<iostream>

usingnamespacestd;

classStudent

{private:

intNo; //定義No為私有數(shù)據(jù)成員

protected:

intAge; //定義Age為保護的數(shù)據(jù)成員

public:

charSex; //定義Sex為公有數(shù)據(jù)成員

Student(intno,intage,charsex)

//定義類Student的構(gòu)造函數(shù){No=no;Age=age;Sex=sex;}

intGetNo(){returnNo;} //返回No的公有成員函數(shù)

intGetAge(){returnAge;} //返回Age的公有成員函數(shù)

voidShowS()

//顯示No、Age、Sex的公有成員函數(shù)

{cout<<"No="<<No<<'\t'<<"Age="<<Age<<'\t'<<"Sex="<<Sex<<endl;}

};

classScore:publicStudent

//由基類Student公有派生出子類Score{

private:

intPhi,Math; //定義類Score的私有數(shù)據(jù)成員

public:

Score(intn,inta,chars,intp,intm):Student(n,a,s)

//類Score的構(gòu)造函數(shù)

{Phi=p;Math=m;}

voidShow(void)

//顯示類Score與其父類Student的數(shù)據(jù)成員值

{cout<<"No="<<GetNo()<<'\t'<<"Age="<<Age<<'\t'<<"Sex="<<Sex<<'\t'<<"Phi="<<Phi<<'\t'<<"Math="<<Math<<endl;

}

};

voidmain(void)

{

Scores(101,20,’M’,90,80); //用類Score定義一個對象s

s.ShowS();

//類Score的對象s調(diào)用基類公有函數(shù)ShowS()

s.Show(); //類Score的對象調(diào)用公有函數(shù)Show()

cout<<"No="<<s.GetNo()<<'\t'<<"Age="<<s.GetAge()<<'\t'<<"Sex="<<s.Sex<<endl;

}程序執(zhí)行后輸出:

No=101Age=20Sex=M

No=101Age=20Sex=MPhi=90Math=80

No=101Age=20Sex=M

從例5-2可以看出,基類Student中的私有數(shù)據(jù)成員No、保護數(shù)據(jù)成員Age、公有數(shù)據(jù)成員Sex在基類中可直接使用,如在基類Student的公有顯示函數(shù)中:

voidShowS()

{cout<<"No="<<No<<'\t'<<"Age="<<Age<<'\t'<<"Sex="<<Sex<<endl;}基類Student中的私有數(shù)據(jù)成員No在派生類Score中不能直接使用,而只能通過公有接口函數(shù)GetNo()訪問。如派生類Score的公有顯示函數(shù)中:

voidShow(void)

{cout<<"No="<<GetNo()<<'\t'<<"Age="<<Age<<'\t'<<"Sex="<<Sex<<

'\t'<<"Phi="<<Phi<<'\t'<<"Math="<<Math<<endl;

}5.2.2私有繼承(PrivateInheritance)

私有繼承的特點是基類的公有成員和保護成員都作為派生類的私有成員,并且不能被這個派生類的子類所訪問。

由于基類中的公有成員Sex、ShowS()與保護成員Age在派生類Score中變?yōu)樗接谐蓡T,因此在類外不能用Score的對象s直接訪問。因而編譯上例程序時,下列語句會出現(xiàn)編譯

錯誤。

s.ShowS();

cout<<"No="<<s.GetNo()<<'\t'<<"Age="<<s.GetAge()<<'\t'<<"Sex="<<s.Sex<<endl;5.2.3保護繼承(ProtectedInheritance)

保護繼承的特點是基類的所有公有成員和保護成員都作為派生類的保護成員,并且只能被它的派生類成員函數(shù)或友元訪問,基類的私有成員仍然是私有的。

將上述3種不同的繼承方式的基類特性與派生類特性列出表格,見表5-1。表5-1不同繼承方式的基類和派生類特性【例5-3】

訪問基類和派生類的函數(shù)。

公有繼承方式、單繼承的例子,程序如下:

#include<iostream>

usingnamespacestd;

classPoint //定義基類

{

public:

voidsetxy(intmyx,intmyy){X=myx;Y=myy;}

voidmovexy(intx,inty){X+=x;Y+=y;}

protected:

intX,Y;};

classCircle:publicPoint

//定義派生類,公有繼承方式

{

public:

voidsetr(intmyx,intmyy,intmyr)

{setxy(myx,myy);R=myr;}

voiddisplay();

protected:

intR;

};voidCircle::display()

{

cout<<"Thepostionofcenteris";

cout<<"("<<X<<","<<Y<<")"<<endl;

cout<<"TheradiusofCircleis"<<R<<endl;

}

intmain()

{

Circlec; //派生類對象

c.setr(4,5,6); cout<<"ThestartdataofCircle:"<<endl;

c.display();

c.movexy(7,8);

cout<<"ThenewdataofCircle:"<<endl;

c.display();

return0;

}程序輸出結(jié)果為:

ThestartdataofCircle:

Thepostionofcenteris(4,5)

TheradiusofCircleis6

ThenewdataofCircle:

Thepostionofcenteris(11,13)

TheradiusofCircleis6【例5-4】

派生類訪問基類的protected成員。

私有繼承例子,程序如下:

#include<iostream>

usingnamespacestd;

classPoint //定義基類

{

public:

voidsetxy(intmyx,intmyy){X=myx;Y=myy;}

voidmovexy(intx,inty){X+=x;Y+=y;}

protected:

intX,Y;

};

classCircle:protectedPoint //定義派生類

{

public:

voidsetr(intmyx,intmyy,intmyr)

{setxy(myx,myy);R=myr;}

voidmovexy(intx,inty){Point::movexy(x,y);}

voiddisplay();

private:

intR;

};

voidCircle::display()

{

cout<<"Thepostionofcenteris";

cout<<"("<<X<<","<<Y<<")"<<endl;

cout<<"TheradiusofCircleis"<<R<<endl;

}

intmain()

{Circlec; //派生類對象

c.setr(4,5,6);

cout<<"ThestartdataofCircle:"<<endl;

c.display();

c.movexy(7,8);

cout<<"ThenewdataofCircle:"<<endl;

c.display();

return0;

}【例5-5】

派生類訪問基類的private成員。

程序如下:

#include<iostream>

usingnamespacestd;

classA

{

inti,j;

public:

voidfun1(inta,intb){i=a;j=b;}

voidfun2()

{cout<<i<<","<<j<<endl;}};

classB:publicA

{

inta;

public:

voidfun3(){cout<<a<<endl;}

voidfun4(intb){a=b/2;}

};

intmain(){

Bdd;

dd.fun1(2,3);

dd.fun2();

dd.fun4(2);

dd.fun3();

return0;

}

運行結(jié)果:

2,3

1程序說明:當基類通過使用private存取說明符被繼承時,基類的所有公有成員和受保護成員變成派生類的私有成員。例如,下面的程序中由于函數(shù)fun1和fun2都是B的私有成員,故不能編譯。

#include<iostream>

usingnamespacestd;

classA

{

inti,j;

public: voidfun1(inta,intb)

{a=i;b=j;}

voidfun2()

{cout<<i<<""<<j<<endl;}

};

classB:privateA

{

inta;

public: voidfun3()

{

cout<<a<<endl;

}

voidfun4(intb)

{b=2*a;}

};voidmain()

{

Bdd;

dd.fun1(2,3);

//這一句執(zhí)行出錯,不能通過編譯

dd.fun2();//這一句出錯,不能通過編譯

dd.fun3();

dd.fun4(4);

}5.3.1派生類的構(gòu)造函數(shù)

(DerivedClassesConstructors)

在派生類對象的成員中,從基類繼承來的成員被封裝為基類子對象,它們的初始化由派生類的構(gòu)造函數(shù)隱含調(diào)用基類構(gòu)造函數(shù)進行初始化;內(nèi)嵌成員對象則隱含調(diào)用成員類的構(gòu)造函數(shù)進行初始化;派生類新增的數(shù)據(jù)成員由派生類在自己定義的構(gòu)造函數(shù)中進行初始化。5.3派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)(DerivedClassesConstructorsandDestructors)派生類構(gòu)造函數(shù)格式如下:

派生類名(參數(shù)總表):基類名1(參數(shù)表1),…,基類名m(參數(shù)表m),

成員對象名1(成員對象參數(shù)表1),…,成員對象名n(成員對象參數(shù)表n)

{

派生類新增成員的初始化;

}

“基類名1(參數(shù)表1),…,基類名m(參數(shù)表m)”稱為基類成員的初始化表。5.3.2派生類構(gòu)造函數(shù)調(diào)用規(guī)則

(DerivedClassesConstructorsCallRules)

1.單繼承的構(gòu)造函數(shù)調(diào)用順序

單繼承時,派生類構(gòu)造函數(shù)調(diào)用的一般次序如下:

(1)調(diào)用基類構(gòu)造函數(shù)。

(2)調(diào)用內(nèi)嵌成員對象的構(gòu)造函數(shù),調(diào)用順序取決于它們在類中定義的順序。

(3)派生類自己的構(gòu)造函數(shù)?!纠?-6】

單繼承機制下構(gòu)造函數(shù)的調(diào)用順序。

程序如下:

#include<iostream>

usingnamespacestd;

classBaseclass

{

public:

Baseclass(inti) //基類的構(gòu)造函數(shù)

{

a=i;

cout<<"constructingBaseclassa="<<a<<endl;

} private:

inta;

};

classDerivedclass:publicBaseclass

{

public:

Derivedclass(inti,intj);

private:

intb;

};Derivedclass::Derivedclass(inti,intj):Baseclass(i)

//派生類的構(gòu)造函數(shù)

{

b=j;

cout<<"constructingDerivedclassb="<<b<<endl;

}

intmain()

{

Derivedclassx(5,6);

return0;

}程序輸出結(jié)果為:

constructingBaseclassa=5

constructingDerivedclassb=6

程序說明:當建立Derivedclass類對象x時,先要調(diào)用基類Baseclass的構(gòu)造函數(shù),輸出“constructingBaseclassa=5”,然后執(zhí)行派生類Derivedclass的構(gòu)造函數(shù),輸出“constructingDerivedclassb=6”?!纠?-7】

包括子對象時,其構(gòu)造函數(shù)的調(diào)用順序。

程序如下:

#include<iostream>

usingnamespacestd;

classBase1 //基類

{

public:

Base1(inti)

{

a=i;cout<<"constructingBase1a="<<a<<endl;

}

private:

inta;

};

classBase2 //子對象f所屬類

{

public:

Base2(inti) {

b=i;

cout<<"constructingBase2b="<<b<<endl;

}

private:

intb;

};

classBase3 //子對象g所屬類

{

public:Base3(inti)

{

c=i;

cout<<"constructingBase3c="<<c<<endl;

}

private:

intc;

};

classDerivedclass:publicBase1 //派生類

{

public:Derivedclass(inti,intj,intk,intm);

private:

intd;

Base2f;

Base3g;

};

Derivedclass::Derivedclass(inti,intj,intk,intm):Base1(i),g(j),f(k)

{

d=m;

cout<<"constructingDerivedclassd="<<d<<endl;

}

intmain()

{

Derivedclassx(5,6,7,8);

return0;

}程序輸出結(jié)果為:

constructingBase1a=5

constructingBase2b=7

constructingBase3c=6

constructingDerivedclassd=8

2.多繼承的構(gòu)造函數(shù)調(diào)用順序

多繼承方式下派生類的構(gòu)造函數(shù)須同時負責該派生類所有基類構(gòu)造函數(shù)的調(diào)用。構(gòu)造函數(shù)調(diào)用順序是:先調(diào)用所有基類的構(gòu)造函數(shù),再調(diào)用派生類的構(gòu)造函數(shù)。處于同一層次的各基類構(gòu)造函數(shù)的調(diào)用順序取決于定義派生類所指定的基類順序,與派生類構(gòu)造函數(shù)中所定義的成員初始化列表順序無關(guān)。【例5-8】

多繼承方式下構(gòu)造函數(shù)的調(diào)用順序。

程序如下:

#include<iostream>

usingnamespacestd;

classBase1 //基類

{

public:

Base1(inti) //基類構(gòu)造函數(shù) {

a=i;

cout<<"constructingBase1a="<<a<<endl;

}

private:

inta;

};

classBase2 //基類

{

public:Base2(inti) //基類構(gòu)造函數(shù)

{

b=i;

cout<<"constructingBase2b="<<b<<endl;

}

private:

intb;

};

classDerivedclass:publicBase1,publicBase2 //派生類

{

public:Derivedclass(inti,intj,intk);

private:

intd;

};

Derivedclass::Derivedclass(inti,intj,intk):Base2(i),Base1(j)

//派生類的構(gòu)造函數(shù)

{d=k;

cout<<"constructingDerivedclassd="<<d<<endl;

}

intmain()

{

Derivedclassx(5,6,7);

return0;

}【例5-9】

有虛基類時,多繼承方式下構(gòu)造函數(shù)的調(diào)用順序。

程序如下:

#include<iostream>

usingnamespacestd;

classbase1

{

public:

base1(){cout<<"constructingbase1"<<endl;}

};classbase2

{

public:

base2(){cout<<"constructingbase2"<<endl;}

};

classderived1:publicbase2,virtualpublicbase1

{public:

derived1(){cout<<"constructingderived1"<<endl;}

};

classderived2:publicbase2,virtualpublicbase1

{

public:

derived2(){cout<<"constructingderived2"<<endl;}};

classDerived3:publicderived1,virtualpublicderived2

{

public:

Derived3(){cout<<"constructingderived3"<<endl;}

};

intmain()

{

Derived3obj;

return0;

}5.3.3派生類的析構(gòu)函數(shù)

(DerivedClassesDestructors)

派生類的析構(gòu)函數(shù)的功能是在該類對象消亡之前進行一些必要的清理工作。析構(gòu)函數(shù)沒有類型,也沒有參數(shù),和構(gòu)造函數(shù)相比情況略為簡單些。【例5-10】

派生類析構(gòu)函數(shù)的調(diào)用順序。

程序如下:

#include<iostream>

usingnamespacestd;

classBase1 //基類

{

public:

Base1(inti) //基類構(gòu)造函數(shù)

{

a=i;cout<<"constructingBase1a="<<a<<endl;

}

~Base1() //基類析構(gòu)函數(shù)

{

cout<<"destructingBase1"<<endl;

}

private:

inta;

};classBase2 //子對象f所屬類

{

public:

Base2(inti) //構(gòu)造函數(shù)

{

b=i;

cout<<"constructingBase2b="<<b<<endl;

}

~Base2() //析構(gòu)函數(shù)

{

cout<<"destructingBase2"<<endl; }

private:

intb;

};

classBase3 //子對象g所屬類

{

public:

Base3(inti) //構(gòu)造函數(shù)

{

c=i;

cout<<"constructingBase3c="<<c<<endl;

}

~Base3() //析構(gòu)函數(shù) {

cout<<"destructingBase3"<<endl;

}

private:

intc;

};

classDerivedclass:publicBase1 //派生類

{

public:

Derivedclass(inti,intj,intk,intm);

~Derivedclass();private:

intd;

Base2f;

Base3g;

};

Derivedclass::Derivedclass(inti,intj,intk,intm):Base1(i),g(j),f(k)

//派生類構(gòu)造函數(shù)

{

d=m;

cout<<"constructingDerivedclassd="<<d<<endl;

}Derivedclass::~Derivedclass() //派生類析構(gòu)函數(shù)

{

cout<<"destructingDerivedclass"<<endl;

}

intmain()

{

Derivedclassx(5,6,7,8);

return0;

}5.4.1多繼承概念(Multi-InheritanceConcept)

多繼承(MultipleInheritance,MI)是指派生類具有兩個或兩個以上的直接基類(DirectClass)。多繼承的一般語法為:

class派生類的類名:訪問區(qū)分符基類1的類名,…,訪問區(qū)分符基類n的類名5.4多繼承(Multi-Inheritance)【例5-11】

多繼承的示例。

程序如下:

#include<iostream>

usingnamespacestd;

classB1

{

protected:

inta;

public:

voiddisplay1()

{cout<<"a="<<a<<endl;

}

};

classB2

{

protected:

intb;

public:

voiddisplay2()

{

cout<<"b="<<b<<endl;

}

};classD:publicB1,publicB2

{

public:

voidsetVars(inta1,inta2)

{

a=a1;

b=a2;

}

};

voidmain()

{Dd;

d.setVars(100,200);

d.display1();

d.display2();

}5.4.2多繼承中的二義性問題及其解決

(AmbiguityinMuti-InheritanceandSolutions)

一般來說,在派生類中對于基類成員的訪問應(yīng)該是唯一的,但是,由于多繼承中派生類擁有多個基類,如果多個基類中擁有同名的成員,那么,派生類在繼承各個基類的成員之后,當調(diào)用該派生類成員時,由于該成員標識符不唯一,出現(xiàn)二義性(ambiguity),編譯器無法確定到底應(yīng)該選擇派生類中的哪一個成員,這種由于多繼承而引起的對類的某個成員訪問出現(xiàn)不唯一的情況就稱為二義性問題。1.派生類成員與基類成員重名

【例5-12】

派生類覆蓋基類中的同名成員。

程序如下:

#include<iostream>

usingnamespacestd;

classA

{

public:

voidfun(){cout<<″調(diào)用類A成員函數(shù)″<<endl;}};

classB

{ public:

voidfun(){cout<<″調(diào)用類B成員函數(shù)″<<endl;}

};

classC

{

public:

voidfunC(){cout<<″調(diào)用類C成員函數(shù)″<<endl;}

};

classD:publicB,publicA,publicC

{public:

voidfun(){cout<<″調(diào)用類D成員函數(shù)″<<endl;}

};

intmain()

{

Dd;

d.fun();

d.funC();

return0;

}

運行結(jié)果:

調(diào)用類D成員函數(shù)

調(diào)用類C成員函數(shù)

2.二義性的產(chǎn)生

如果只是基類與基類之間的成員重名,在這種情況下,系統(tǒng)將無法自行決定調(diào)用的是哪一個函數(shù),這就存在二義性問題。比如:

classA

{

public:

voidfun(){…}

};

classB

{public:

voidfun(){…}

};

classC:publicB,publicA{ };

intmain()

{

Cc;

c.fun();//調(diào)用存在二義性

return0;

}

【例5-13】

定義一個小客車類Car和一個小貨車類Wagon,它們共同派生出一個客貨兩用車類StationWagon。StationWagon既繼承了小客車的特征,有座位seat,可以載客,又繼承了小貨車的特征,有裝載車廂load,可以載貨。程序?qū)崿F(xiàn)如下:

classCar //小客車類

{

private:

intpower; //馬力

intseat; //座位

public:

Car(intpower,intseat)

{

this->power=power,this->seat=seat;

}

voidshow()

{

cout<<"carpower:"<<power<<"seat:"<<seat<<endl;

}

};classWagon //小貨車類

{

private:

intpower; //馬力

intload; //裝載量

public:

Wagon(intpower,intload)

{

this->power=power,this->load=load;

}

voidshow()

{cout<<"wagonpower:"<<power<<"load:"<<load<<endl;

}

};

classStationWagon:publicCar,publicWagon

//客貨兩用車類

{

public:

StationWagon(intpower,intseat,intload):

Wagon(power,load),Car(power,seat) {

}

voidShowSW()

{

cout<<"StationWagon:"<<endl;

Car::show();

Wagon::show();}

};

voidmain()

{

StationWagonSW(105,3,8);

//SW.show(); //錯誤,出現(xiàn)二義性

SW.ShowSW();

}程序運行結(jié)果:

StationWagon:

carpower:105seat:3

wagonpower:105load:8

3.二義性問題的解決方法

二義性問題通常有兩種解決方法:

(1)成員名限定:通過類的作用域分辨符明確限定出現(xiàn)歧義的成員是繼承自哪一個基類。

(2)成員重定義:在派生類中新增一個與基類中成員相同的成員,由于同名覆蓋,程序?qū)⒆詣舆x擇派生類新增的成員。【例5-14】

消除公共基類二義性程序。

程序如下:

#include<iostream.h>

classA

{

public:

inta;

voidfun()

{cout<<"類A成員函數(shù)被正確調(diào)用"<<endl;}};

classB:publicA

{

public:

voiddisplay()

{

cout<<"Ab="<<a<<endl;

}

};classC:publicA

{

public:

voiddisplay()

{

cout<<"Ac="<<a<<endl;

}

};

classD:publicB,publicC

{

public:voiddisplay()

{

B::display();

C::display();

}

};

intmain()

{Dd;

d.B::a=1;

d.C::a=2;

d.display();

d.B::fun();

d.C::fun();

return0;

}程序運行結(jié)果:

Ab=1

Ac=2

類A成員函數(shù)被正確調(diào)用。

為了消除公共基類的二義性,需要采用派生類的直接基類名來限定,而不是需要訪問成員所在的類的類名?!纠?-15】

類型不同或訪問權(quán)限不同產(chǎn)生二義性的示例。

程序如下:

#include<iostream>

usingnamespacestd;

classA

{

public:

intf1(int){return0;}

voidf2(){}

intf3(){return0;}

};classB

{

public:

intf1(){return0;}

doublef2(){return0;}

private:

intf3(){return0;}

}; classC:publicA,publicB{};

intmain()

{

Cc;

inta=c.f1(1); //存在二義性

c.f2(); //存在二義性

intb=c.f3(); //存在二義性

return0;

}5.4.3多繼承中構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用順序

(ConstructorsandDestructorsunder?Multi-Inheritance

CallOrder)

在多繼承的情況下,基類及派生類的構(gòu)造函數(shù)是按以下順序被調(diào)用的:

(1)按基類被列出的順序逐一調(diào)用基類的構(gòu)造函數(shù)。

(2)如果該派生類存在成員對象,則調(diào)用成員對象的構(gòu)造函數(shù)。

(3)若存在多個成員對象,按它們被列出的順序逐一調(diào)用。

(4)最后調(diào)用派生類的構(gòu)造函數(shù)?!纠?-16】

構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用。

程序如下:

#include<iostream>

usingnamespacestd;

classHairColor

{

private:

intcolor;

public:HairColor(intcolor)

{

cout<<"ConstructorofclassHairColorcalled"<<endl;

this->color=color;

}

~HairColor()

{

cout<<"DestructorofclassHairColorcalled"<<endl;

}

};classHorse

{

public:

Horse()

{

cout<<"ConstructorofclassHorsecalled"<<endl;

}

~Horse()

{

cout<<"DestructorofclassHorsecalled"<<endl;}

};

classDonkey

{

public:

Donkey()

{

cout<<"ConstructorofclassDonkeycalled"<<endl;

}

~Donkey()

{cout<<"DestructorofclassDonkeycalled"<<endl;

}

};

classMule:publicHorse,publicDonkey

{

?private:

HairColorhcInstance;

//hcInstance是HairColor類的成員

?public:

Mule(intcolor):hcInstance(color){

cout<<"ConstructorofclassMulecalled"<<endl;

}

~Mule()

{

cout<<"DestructorofclassMulecalled"<<endl;

}

};

voidmain()

{

MulemuleInstance(100);

cout<<"Programover"<<endl;

}程序運行結(jié)果:

ConstructorofclassHorsecalled

ConstructorofclassDonkeycalled

ConstructorofclassHairColorcalled

ConstructorofclassMuleCalled

Programover

DestructorofclassMulecalled

DestructorofclassHairColorcalled

DestructorofclassDonkeycalled

DestructorofclassHorsecalled5.5.1多繼承派生的基類拷貝

(BaseClassesCopyunderMulti-Inheritance)

若類B與類C由類A公有派生,而類D由類B與類C公有派生,則類D中將包含類A的兩個拷貝(如圖5-5所示)。這種同一個基類在派生類中產(chǎn)生多個拷貝不僅多占用了存儲空間,而且可能會造成多個拷貝數(shù)據(jù)的不一致。下面舉例說明。5.5虛基類(VirtualBaseClasses)圖5-5一個公共基類在派生類中產(chǎn)生兩個拷貝【例5-17】

一個公共基類在派生類中產(chǎn)生兩個拷貝。

程序如下:

#include<iostream>

usingnamespacestd;

classA

{

?public:

intx;

A(inta)

{x=a;}

};classB:publicA

//由公共基類A派生出類B

{public:

inty;

B(inta,intb):A(b)

{

y=a;

?}

};classC:publicA

//由公共基類A派生出類C

{

public:

intz;

C(inta,intb):A(b)

{

z=a;

?}

};classD:publicB,publicC

//由基類B、C派生出類D

{

?public:

intm;

D(inta,intb,intd,inte,intf):B(a,b),C(d,e)

{

m=f;

}

voidPrint()

{

cout<<"x="<<B::x<<'\t'<<"y="<<y<<endl;

cout<<"x="<<C::x<<'\t'<<"z="<<z<<endl;

cout<<”m=”<<m<<endl;

}

};voidmain(void)

{

Dd1(100,200,300,400,500);

d1.Print();

}

程序執(zhí)行后輸出:

x=200

y=100

x=400

z=300

m=500根據(jù)輸出的結(jié)果可以清楚地看出,在類D中包含了公共基類A的兩個不同拷貝。這種派生關(guān)系產(chǎn)生的類體系如圖5-4所示。用類D定義對象d1時,系統(tǒng)為d1數(shù)據(jù)成員分配的空間如圖5-6所示。圖5-6對象d1的數(shù)據(jù)成員5.5.2虛基類的定義

(DefinitionofVirtualBaseClasses)

在多重派生的過程中,欲使公共的基類在派生中只有一個拷貝,可將此基類說明成虛基類。虛基類的定義格式為:

class<派生類名>:virtual<access><虛基類名>

{…};或

class<派生類名>:<access>virtual<虛基類名>

{…};

其中,關(guān)鍵詞virtual可放在訪問權(quán)限之前,也可放在訪問權(quán)限之后,并且關(guān)鍵詞只對緊隨其后的基類名起作用。【例5-18】

定義虛基類,使派生類中只有基類的一個拷貝。

程序如下:

#include<iostream>

usingnamespacestd;

classA

{

public:

intx;

A(inta=0)

{x=a;

?}

};

classB:virtualpublicA

//由公共基類A派生出類B

{

public:

inty;

B(inta,intb):A(b)

{

y=a;

?}

};classC:publicvirtualA

//由公共基類A派生出類C

{

public:

intz;

C(inta,intb):A(b)

{

z=a;

?}

};classD:publicB,publicC

//由基類B、C派生出類D

{

public:

intm;

D(inta,intb,intd,inte,intf):B(a,b),C(d,e){m=f;}

voidPrint()

{

cout<<"x="<<x<<'\t'<<"y="<<y<<endl;

cout<<"x="<<x<<'\t'<<"z="<<z<<endl;

cout<<"m="<<m<<endl;

}

};

voidmain(void){

Dd1(100,200,300,400,500);

d1.Print();

d1.x=400;

d1.Print();

}本例中定義的派生關(guān)系產(chǎn)生的類體系如圖5-4所示。

執(zhí)行程序后輸出:

x=0

y=100

x=0

z=300

m=500

x=400

y=100

x=400

z=300

m=5005.5.3虛基類的構(gòu)造與析構(gòu)

(ConstructingandDestructingVirtualBase

Classes)

C++?將建立對象時所使用的派生類稱為最遠派生類。對于虛基類而言,由于最遠派生類對象中只有一個公共虛基類子對象,為了初始化該公共基類子對象,最遠派生類的構(gòu)造函數(shù)要調(diào)用該公共基類的構(gòu)造函數(shù),而且只能被調(diào)用一次。所謂賦值兼容規(guī)則指的是不同類型的對象間允許相互賦值的規(guī)定。面向?qū)ο蟪绦蛟O(shè)計語言中,在公有派生的情況下,允許將派生類的對象賦值給基類的對象,但反過來卻不行,即不允許將基類的對象賦值給派生類的對象。這是因為一個派生類對象的存儲空間總是大于它的基類對象的存儲空間。若將基類對象賦值給派生類對象,這個派生類對象中將會出現(xiàn)一些未賦值的不確定成員。5.6賦值兼容規(guī)則(CompatibleAssignmentRules)允許將派生類的對象賦值給基類的對象,有以下三種具體做法:

(1)直接將派生類對象賦值給基類對象。例如:

BaseobjB;

DerivedobjD; //假設(shè)Derived已定義為Base的派生類

ObjB=objD; //合法

ObjD=objB; //非法(2)定義派生類對象的基類引用。例如:

Base&b=objD;

(3)用指向基類對象的指針指向它的派生類對象。例如:

Base*pb=&objD;

賦值兼容規(guī)則是指在需要基類對象的任何地方都可以使用公有派生類的對象來替代。通過公有繼承,派生類得到了基類中除構(gòu)造函數(shù)、析構(gòu)函數(shù)之外的所有成員,而且所有成員的訪問控制屬性也和基類完全相同。賦值兼容規(guī)則中所指的替代包括以下幾種情況:

(1)派生類的對象可以賦值給基類對象。

(2)派生類的對象可以初始化基類的引用。

(3)派生類對象的地址可以賦給指向基類的指針。在如下程序中,b1為B類的對象,d1為D的對象。

classB

{…};

classD:publicB

{…};

voidmain()

{ Bb1,*pb1;

Dd1;

}【例5-19】

賦值兼容規(guī)則示例。

本例中,基類B0以公有方式派生出B1類,B1類再作為基類以公有方式派生出D1類,基類B0中定義了成員函數(shù)display(),在派生類中對這個成員函數(shù)進行了覆蓋。程序代碼如下:

#include<iostream>

usingnamespacestd;

classB0

{public:

voiddisplay()

{

cout<<"B0::display()"<<endl;

}

};

classB1:publicB0

{

public:

voiddisplay()

{cout<<"B1::display()"<<endl;

}

};

classD1:publicB1

{

voiddisplay()

{

cout<<"D1::display()"<<endl;

}

};

voidfun(B0*ptr)

{

ptr->display();}

voidmain() //主函數(shù)

{

B0b0; //聲明B0類對象

B1b1; //聲明B1類對象

D1d1; //聲明D1類對象

B0*p; //聲明B0類指針

p=&b0; //B0類指針指向B0類對象

fun(p);p=&b1; //B0類指針指向B1類對象

fun(p);

p=&d1; //B0類指針指向D1類對象

fun(p);

}

程序運行結(jié)果:

B0::display()

B0::display()

B0::display()【例5-20】

編寫一個程序分別計算出球、圓柱和圓錐的表面積和體積。

此題的完整程序如下:

#include<iostream>

#include<math.h>

usingnamespacestd;

constdoublePI=3.1415926;

classCircle

{ 5.7程序舉例(ProgramExamples)protected:

doubler;

public:

Circle(doubleradius=0)

{r=radius;

}

doubleArea()

{returnPI*r*r;

}doubleVolume()

{return0;

}

};

classSphere:publicCircle //球體類

{public:

Sphere(doubleradius=0):Circle(radius){}

doubleArea()

{return4*PI*r*r;

}doubleVolume()

{return4*PI*pow(r,3)/3;

}

};

classCylinder:publicCircle

{

doubleh;

public:Cylinder(doubleradius=0,doubleheight=0):Circle(radius)

{h=height;

}

doubleArea()

{return2*PI*r*(r+h);

}

doubleVolume()

{returnPI*r*r*h;

}

};classCone:publicCircle //圓錐體類

{

doubleh;

public:

Cone(doubleradius=0,doubleheight=0):Circle(radius)

{h=height;

}

doubleArea()

{doublel=sqrt(h*h+r*r);returnPI*r*(r+l);

}doubleVolume()

{returnPI*r*r*h/3;

}

};

voidmain()

{Circler1(2);

Spherer2(2);

Cylinderr3(2,3);

Coner4(2,3);

cout<<"Circle:"<<r1.Area()<<''<<r1.Volume()<<endl;

cout<<"Sphere:"<<r2.Area()<<''<<r2.Volume()<<endl;

cout<<"Clinder:"<<r3.Area()<<''<<r3.Volume()<<endl;

cout<<"Cone:"<<r4.Area()<<''<<r4.Volume()<<endl;

}程序運行結(jié)果:

Circle:12.56640

Shhere:50.265533.5103

Clinder:62.831937.6991

Cone:35.220712.5664【例5-21】

一個小型公司的人員信息管理系統(tǒng)。

(1)問題的提出。

某小型公司,主要有4類人員:經(jīng)理、兼職技術(shù)人員、銷售經(jīng)理和兼職推銷員?,F(xiàn)在,需要存儲這些人員的姓名、編號、級別、當月薪水,計算月薪總額并顯示全部信息。

(2)類設(shè)計。

根據(jù)上述需求,設(shè)計一個基類employee,然后派生出technician(兼職技術(shù)人員)類、manager(經(jīng)理)類和salesman(兼職推銷員)類。由于銷售經(jīng)理既是經(jīng)理又是銷售人員,兼具兩類人員的特點,因此同時繼承manager和salesman兩個類。由于salesmanager類的兩個基類又有公共基類employee,為了避免二義性,這里將employee設(shè)計為虛基類。程序代碼如下:

//employee.h

classemployee

{

protected:

char*name; //姓名

intindividualEmpNo; //個人編號

int grade; //級別floataccumPay; //月薪總額

staticintemployeeNo; //本公司職員編號目前最大值

public:

employee(); //構(gòu)造函數(shù)

~employee(); //析構(gòu)函數(shù)

voidpay(); //計算月薪函數(shù)

voidpromote(int); //升級函數(shù)

voiddisplayStatus(); //顯示人員信息

};classtechnician:publicemployee //兼職技術(shù)人員數(shù)

{

private:

floathourlyRate; //每小時酬金

intworkHours; //當月工作時數(shù)

public:

technician(); //構(gòu)造函數(shù)

voidpay(); //計算月薪函數(shù)

voiddisplayStatus(); //顯示人員信息

};classsalesman:virtualpublicemployee //兼職推銷員類

{

protected:

floatCommRate; //按銷售額提取酬金的百分比

floatsales; //當月銷售額

public:

salesman(); //構(gòu)造函數(shù)

voidpay(); //計算月薪函數(shù)

voiddisplayStatus(); //顯示人員信息};

classmanager:virtualpublicemployee //經(jīng)理類

{

protected:

floatmonthlyPay; //固定月薪數(shù)

public:

manager(); //構(gòu)造函數(shù)

voidpay(); //計算月薪函數(shù)

voiddisplayStatus(); //顯示人員信息};

classsalesmanager:publicmanager,publicsalesman

//銷售經(jīng)理類

{

public:

salesmanager(); //構(gòu)造函數(shù)

voidpay(); //計算月薪函數(shù)

voiddisplaySta

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論