Ch1114-《C++程序設(shè)計》(譚浩強)_第1頁
Ch1114-《C++程序設(shè)計》(譚浩強)_第2頁
Ch1114-《C++程序設(shè)計》(譚浩強)_第3頁
Ch1114-《C++程序設(shè)計》(譚浩強)_第4頁
Ch1114-《C++程序設(shè)計》(譚浩強)_第5頁
已閱讀5頁,還剩326頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第11章繼承與派生第12章多態(tài)性與虛函數(shù)第13章輸入輸出流第14章C++工具第4篇

面向?qū)ο蟮某绦蛟O(shè)計第11章繼承與派生11.1繼承與派生的概念11.2派生類的聲明方式11.3派生類的構(gòu)成11.4派生類成員的訪問屬性11.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)11.6多重繼承11.7基類與派生類的轉(zhuǎn)換11.8繼承與組合11.9繼承在軟件開發(fā)中的重要意義面向?qū)ο蟪绦蛟O(shè)計有4個主要特點:抽象、封裝、繼承和多態(tài)性。要較好地進行面向?qū)ο蟪绦蛟O(shè)計,還必須了解面向?qū)ο蟪绦蛟O(shè)計另外兩個重要特征——繼承性和多態(tài)性。在本章中主要介紹有關(guān)繼承的知識,在第12章中將介紹多態(tài)性。面向?qū)ο蠹夹g(shù)強調(diào)軟件的可重用性〔softwarereusability〕。C++語言提供了類的繼承機制,解決了軟件重用問題。在C++中可重用性是通過繼承〔inheritance〕這一機制來實現(xiàn)的。繼承是C++的一個重要組成局部。一個類中包含了假設(shè)干數(shù)據(jù)成員和成員函數(shù)。在不同的類中,數(shù)據(jù)成員和成員函數(shù)是不相同的。但有時兩個類的內(nèi)容根本相同或有一局部相同。利用原來聲明的類Student作為根底,再加上新的內(nèi)容即可,以減少重復(fù)的工作量。C++提供的繼承機制就是為了解決這個問題。在第8章已舉了馬的例子來說明繼承的概念。見圖11.1示意。11.1繼承與派生的概念在C++中,所謂“繼承”就是在一個已存在的類的根底上建立一個新的類。已存在的類〔例如“馬”〕稱為“基類〔baseclass〕”或“父類〔fatherclass〕”。新建立的類〔例如“公馬”〕稱為“派生類〔derivedclass〕”或“子類〔sonclass〕”。見圖11.2示意。圖11.1圖11.2一個新類從已有的類那里獲得其已有特性,這種現(xiàn)象稱為類的繼承。通過繼承,一個新建子類從已有的父類那里獲得父類的特性。從另一角度說,從已有的類〔父類〕產(chǎn)生一個新的子類,稱為類的派生。類的繼承是用已有的類來建立專用類的編程技術(shù)。派生類繼承了基類的所有數(shù)據(jù)成員和成員函數(shù),并可以對成員作必要的增加或調(diào)整。一個基類可以派生出多個派生類,每一個派生類又可以作為基類再派生出新的派生類,因此基類和派生類是相對而言的。以上介紹的是最簡單的情況:一個派生類只從一個基類派生,這稱為單繼承〔singleinheritance〕,這種繼承關(guān)系所形成的層次是一個樹形結(jié)構(gòu),可以用圖11.3表示。圖11.3請注意圖中箭頭的方向,在本書中約定,箭頭表示繼承的方向,從派生類指向基類。一個派生類不僅可以從一個基類派生,也可以從多個基類派生。一個派生類有兩個或多個基類的稱為多重繼承〔multipleinheritance〕,這種繼承關(guān)系所形成的結(jié)構(gòu)如圖11.4所示。圖11.4關(guān)于基類和派生類的關(guān)系,可以表述為:派生類是基類的具體化,而基類那么是派生類的抽象。圖11.5假設(shè)已經(jīng)聲明了一個基類Student,在此根底上通過單繼承建立一個派生類Student1:classStudent1:publicStudent//聲明基類是Student{public:voiddisplay_1〔〕//新增加的成員函數(shù){cout<<″age:″<<age<<endl;cout<<″address:″<<addr<<endl;}private:intage;//新增加的數(shù)據(jù)成員stringaddr;//新增加的數(shù)據(jù)成員};11.2派生類的聲明方式基類名前面有public的稱為“公用繼承〔publicinheritance〕”。聲明派生類的一般形式為class派生類名:[繼承方式]基類名{派生類新增加的成員};繼承方式包括:public〔公用的〕,private〔私有的〕和protected〔受保護的〕,此項是可選的,如果不寫此項,那么默認為private〔私有的〕。派生類中的成員包括從基類繼承過來的成員和自己增加的成員兩大局部。在基類中包括數(shù)據(jù)成員和成員函數(shù)〔或稱數(shù)據(jù)與方法〕兩局部,派生類分為兩大局部:一局部是從基類繼承來的成員,另一局部是在聲明派生類時增加的局部。每一局部均分別包括數(shù)據(jù)成員和成員函數(shù)。11.3派生類的構(gòu)成圖11.6實際上,并不是把基類的成員和派生類自己增加的成員簡單地加在一起就成為派生類。構(gòu)造一個派生類包括以下3局部工作:〔1〕從基類接收成員。派生類把基類全部的成員〔不包括構(gòu)造函數(shù)和析構(gòu)函數(shù)〕接收過來,也就是說是沒有選擇的,不能選擇接收其中一局部成員,而舍棄另一局部成員。要求我們根據(jù)派生類的需要慎重選擇基類,使冗余量最小。事實上,有些類是專門作為基類而設(shè)計的,在設(shè)計時充分考慮到派生類的要求?!?〕調(diào)整從基類接收的成員。接收基類成員是程序人員不能選擇的,但是程序人員可以對這些成員作某些調(diào)整。〔3〕在聲明派生類時增加的成員。這局部內(nèi)容是很重要的,它表達了派生類對基類功能的擴展。要根據(jù)需要仔細考慮應(yīng)當增加哪些成員,精心設(shè)計。此外,在聲明派生類時,一般還應(yīng)當自己定義派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù),因為構(gòu)造函數(shù)和析構(gòu)函數(shù)是不能從基類繼承的。派生類是基類定義的延續(xù)??梢韵嚷暶饕粋€基類,在此基類中只提供某些最根本的功能,而另外有些功能并未實現(xiàn),然后在聲明派生類時參加某些具體的功能,形成適用于某一特定應(yīng)用的派生類。通過對基類聲明的延續(xù),將一個抽象的基類轉(zhuǎn)化成具體的派生類。因此,派生類是抽象基類的具體實現(xiàn)。既然派生類中包含基類成員和派生類自己增加的成員,就產(chǎn)生了這兩局部成員的關(guān)系和訪問屬性的問題。在建立派生類的時候,并不是簡單地把基類的私有成員直接作為派生類的私有成員,把基類的公用成員直接作為派生類的公用成員。實際上,對基類成員和派生類自己增加的成員是按不同的原那么處理的。11.4派生類成員的訪問屬性具體說,在討論訪問屬性時,要考慮以下幾種情況:〔1〕基類的成員函數(shù)訪問基類成員?!?〕派生類的成員函數(shù)訪問派生類自己增加的成員。〔3〕基類的成員函數(shù)訪問派生類的成員。〔4〕派生類的成員函數(shù)訪問基類的成員。〔5〕在派生類外訪問派生類的成員?!?〕在派生類外訪問基類的成員。對于第〔1〕和第〔2〕種情況,比較簡單,按第8章介紹過的規(guī)那么處理,即:基類的成員函數(shù)可以訪問基類成員,派生類的成員函數(shù)可以訪問派生類成員。私有數(shù)據(jù)成員只能被同一類中的成員函數(shù)訪問,公用成員可以被外界訪問。第〔3〕種情況也比較明確,基類的成員函數(shù)只能訪問基類的成員,而不能訪問派生類的成員。第〔5〕種情況也比較明確,在派生類外可以訪問派生類的公用成員,而不能訪問派生類的私有成員。對于第〔4〕和第〔6〕種情況,就稍微復(fù)雜一些,也容易混淆。這些牽涉到如何確定基類的成員在派生類中的訪問屬性的問題,不僅要考慮對基類成員所聲明的訪問屬性,還要考慮派生類所聲明的對基類的繼承方式,根據(jù)這兩個因素共同決定基類成員在派生類中的訪問屬性。前面已提到:在派生類中,對基類的繼承方式可以有public〔公用的〕,private〔私有的〕和protected〔保護的〕3種。不同的繼承方式?jīng)Q定了基類成員在派生類中的訪問屬性。簡單地說:〔1〕公用繼承〔publicinheritance〕基類的公用成員和保護成員在派生類中保持原有訪問屬性,其私有成員仍為基類私有。〔2〕私有繼承〔privateinheritance〕基類的公用成員和保護成員在派生類中成了私有成員。其私有成員仍為基類私有?!?〕受保護的繼承〔protectedinheritance〕基類的公用成員和保護成員在派生類中成了保護成員,其私有成員仍為基類私有。保護成員的意思是:不能被外界引用,但可以被派生類的成員引用,具體的用法將在稍后介紹。在定義一個派生類時將基類的繼承方式指定為public的,稱為公用繼承,用公用繼承方式建立的派生類稱為公用派生類〔publicderivedclass〕,其基類稱為公用基類〔publicbaseclass〕。采用公用繼承方式時,基類的公用成員和保護成員在派生類中仍然保持其公用成員和保護成員的屬性,而基類的私有成員在派生類中并沒有成為派生類的私有成員,它仍然是基類的私有成員,只有基類的成員函數(shù)可以引用它,而不能被派生類的成員函數(shù)引用,因此就成為派生類中的不可訪問的成員。公用基類的成員在派生類中的訪問屬性見書中表11.1。11.4.1公用繼承例11.1訪問公有基類的成員。下面寫出類的聲明局部:ClassStudent//聲明基類{public://基類公用成員voidget_value〔〕{cin>>num>>name>>sex;}voiddisplay〔〕{cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}private://基類私有成員intnum;stringname;charsex;};classStudent1:publicStudent//以public方式聲明派生類Student1{public:voiddisplay_1〔〕{cout<<″num:″<<num<<endl;//企圖引用基類的私有成員,錯誤cout<<″name:″<<name<<endl;//企圖引用基類的私有成員,錯誤cout<<″sex:″<<sex<<endl;//企圖引用基類的私有成員,錯誤cout<<″age:″<<age<<endl;//引用派生類的私有成員,正確cout<<″address:″<<addr<<endl;}//引用派生類的私有成員,正確private:intage;stringaddr;};由于基類的私有成員對派生類來說是不可訪問的,因此在派生類中的display_1函數(shù)中直接引用基類的私有數(shù)據(jù)成員num,name和sex是不允許的。只能通過基類的公用成員函數(shù)來引用基類的私有數(shù)據(jù)成員。可以將派生類Student1的聲明改為classStudent1:publicStudent//以public方式聲明派生類Student1{public:voiddisplay_1〔〕{cout<<″age:″<<age<<endl;//引用派生類的私有成員,正確cout<<″address:″<<addr<<endl;//引用派生類的私有成員,正確}private:intage;stringaddr;};然后在main函數(shù)中分別調(diào)用基類的display函數(shù)和派生類中的display_1函數(shù),先后輸出5個數(shù)據(jù)??梢赃@樣寫main函數(shù)〔假設(shè)對象stud中已有數(shù)據(jù)〕:intmain〔〕{Student1stud;//定義派生類Student1的對象stud┆stud.display〔〕;//調(diào)用基類的公用成員函數(shù),輸出基類中3個數(shù)據(jù)成員的值stud.display_1〔〕;//調(diào)用派生類的公用成員函數(shù),輸出派生類中兩個數(shù)據(jù)成員的值return0;}請根據(jù)上面的分析,寫出完整的程序,程序中應(yīng)包括輸入數(shù)據(jù)的函數(shù)。實際上,程序還可以改進,在派生類的display_1函數(shù)中調(diào)用基類的display函數(shù),在主函數(shù)中只要寫一行:stud.display_1〔〕;即可輸出5個數(shù)據(jù)。在聲明一個派生類時將基類的繼承方式指定為private的,稱為私有繼承,用私有繼承方式建立的派生類稱為私有派生類〔privatederivedclass〕,其基類稱為私有基類〔privatebaseclass〕。私有基類的公用成員和保護成員在派生類中的訪問屬性相當于派生類中的私有成員,即派生類的成員函數(shù)能訪問它們,而在派生類外不能訪問它們。私有基類的私有成員在派生類中成為不可訪問的成員,只有基類的成員函數(shù)可以引用它們。一個基類成員在基類中的訪問屬性和在派生類中的訪問屬性可能是不同的。私有基類的成員在私有派生類中的訪問屬性見書中表11.2。11.4.2私有繼承圖11.7圖11.7表示了各成員在派生類中的訪問屬性。對表11.2的規(guī)定不必死記,只需理解:既然聲明為私有繼承,就表示將原來能被外界引用的成員隱藏起來,不讓外界引用,因此私有基類的公用成員和保護成員理所當然地成為派生類中的私有成員。私有基類的私有成員按規(guī)定只能被基類的成員函數(shù)引用,在基類外當然不能訪問他們,因此它們在派生類中是隱蔽的,不可訪問的。對于不需要再往下繼承的類的功能可以用私有繼承方式把它隱蔽起來,這樣,下一層的派生類無法訪問它的任何成員??梢灾?一個成員在不同的派生層次中的訪問屬性可能是不同的。它與繼承方式有關(guān)。例11.2將例11.1中的公用繼承方式改為用私有繼承方式〔基類Student不改〕。可以寫出私有派生類如下:classStudent1:privateStudent//用私有繼承方式聲明派生類Student1{public:voiddisplay_1〔〕//輸出兩個數(shù)據(jù)成員的值{cout<<″age:″<<age<<endl;//引用派生類的私有成員,正確cout<<″address:″<<addr<<endl;}//引用派生類的私有成員,正確private:intage;stringaddr;};請分析下面的主函數(shù):intmain〔〕{Student1stud1;//定義一個Student1類的對象stud1stud1.display〔〕;//錯誤,私有基類的公用成員函數(shù)在派生類中是私有函數(shù)stud1.display_1〔〕;//正確。Display_1函數(shù)是Student1類的公用函數(shù)stud1.age=18;//錯誤。外界不能引用派生類的私有成員return0;}可以看到:〔1〕不能通過派生類對象〔如stud1〕引用從私有基類繼承過來的任何成員〔如stud1.display〔〕或stud1.num〕?!?〕派生類的成員函數(shù)不能訪問私有基類的私有成員,但可以訪問私有基類的公用成員〔如stud1.display_1函數(shù)可以調(diào)用基類的公用成員函數(shù)display,但不能引用基類的私有成員num〕。有沒有方法調(diào)用私有基類的公用成員函數(shù),從而引用私有基類的私有成員呢?有。應(yīng)當注意到:雖然在派生類外不能通過派生類對象調(diào)用私有基類的公用成員函數(shù),但可以通過派生類的成員函數(shù)調(diào)用私有基類的公用成員函數(shù)〔此時它是派生類中的私有成員函數(shù),可以被派生類的任何成員函數(shù)調(diào)用〕??蓪⑸厦娴乃接信缮惖某蓡T函數(shù)定義改寫為voiddisplay_1〔〕//輸出5個數(shù)據(jù)成員的值{display〔〕://調(diào)用基類的公用成員函數(shù),輸出3個數(shù)據(jù)成員的值cout<<″age:″<<age<<endl;//輸出派生類的私有數(shù)據(jù)成員cout<<″address:″<<addr<<endl;}//輸出派生類的私有數(shù)據(jù)成員main函數(shù)可改寫為intmain〔〕{Student1stud1;stud1.display_1〔〕;//display_1函數(shù)是派生類Student1類的公用函數(shù)return0;}這樣就能正確地引用私有基類的私有成員。可以看到,本例采用的方法是:①在main函數(shù)中調(diào)用派生類中的公用成員函數(shù)stud1.display_1;②通過該公用成員函數(shù)調(diào)用基類的公用成員函數(shù)display〔它在派生類中是私有函數(shù),可以被派生類中的任何成員函數(shù)調(diào)用〕;③通過基類的公用成員函數(shù)display引用基類中的數(shù)據(jù)成員。請根據(jù)上面的要求,補充和完善上面的程序,使之成為完整、正確的程序。程序中應(yīng)包括輸入數(shù)據(jù)的函數(shù)。由于私有派生類限制太多,使用不方便,一般不常使用。由protected聲明的成員稱為“受保護的成員”,或簡稱“保護成員”。從類的用戶角度來看,保護成員等價于私有成員。但有一點與私有成員不同,保護成員可以被派生類的成員函數(shù)引用。見圖11.8示意。圖11.811.4.3保護成員和保護繼承如果基類聲明了私有成員,那么任何派生類都是不能訪問它們的,假設(shè)希望在派生類中能訪問它們,應(yīng)當把它們聲明為保護成員。如果在一個類中聲明了保護成員,就意味著該類可能要用作基類,在它的派生類中會訪問這些成員。在定義一個派生類時將基類的繼承方式指定為protected的,稱為保護繼承,用保護繼承方式建立的派生類稱為保護派生類〔protectedderivedclass〕,其基類稱為受保護的基類〔protectedbaseclass〕,簡稱保護基類。保護繼承的特點是:保護基類的公用成員和保護成員在派生類中都成了保護成員,其私有成員仍為基類私有。也就是把基類原有的公用成員也保護起來,不讓類外任意訪問。將表11.1和表11.2綜合起來,并增加保護繼承的內(nèi)容,見書中表11.3。保護基類的所有成員在派生類中都被保護起來,類外不能訪問,其公用成員和保護成員可以被其派生類的成員函數(shù)訪問。比較一下私有繼承和保護繼承〔也就是比較在私有派生類中和在保護派生類中的訪問屬性〕,可以發(fā)現(xiàn),在直接派生類中,以上兩種繼承方式的作用實際上是相同的:在類外不能訪問任何成員,而在派生類中可以通過成員函數(shù)訪問基類中的公用成員和保護成員。但是如果繼續(xù)派生,在新的派生類中,兩種繼承方式的作用就不同了。例如,如果以公用繼承方式派生出一個新派生類,原來私有基類中的成員在新派生類中都成為不可訪問的成員,無論在派生類內(nèi)或外都不能訪問,而原來保護基類中的公用成員和保護成員在新派生類中為保護成員,可以被新派生類的成員函數(shù)訪問。從表11.3可知:基類的私有成員被派生類繼承后變?yōu)椴豢稍L問的成員,派生類中的一切成員均無法訪問它們。如果需要在派生類中引用基類的某些成員,應(yīng)當將基類的這些成員聲明為protected,而不要聲明為private。如果善于利用保護成員,可以在類的層次結(jié)構(gòu)中找到數(shù)據(jù)共享與成員隱蔽之間的結(jié)合點。既可實現(xiàn)某些成員的隱蔽,又可方便地繼承,能實現(xiàn)代碼重用與擴充。通過以上的介紹,可以知道:〔1〕在派生類中,成員有4種不同的訪問屬性:①公用的,派生類內(nèi)和派生類外都可以訪問。②受保護的,派生類內(nèi)可以訪問,派生類外不能訪問,其下一層的派生類可以訪問。③私有的,派生類內(nèi)可以訪問,派生類外不能訪問。④不可訪問的,派生類內(nèi)和派生類外都不能訪問??梢杂脮斜?1.4表示。需要說明的是:①這里所列出的成員的訪問屬性是指在派生類中所獲得的訪問屬性。②所謂在派生類外部,是指在建立派生類對象的模塊中,在派生類范圍之外。③如果本派生類繼續(xù)派生,那么在不同的繼承方式下,成員所獲得的訪問屬性是不同的,在本表中只列出在下一層公用派生類中的情況,如果是私有繼承或保護繼承,讀者可以從表11.3中找到答案?!?〕類的成員在不同作用域中有不同的訪問屬性,對這一點要十分清楚。下面通過一個例子說明怎樣訪問保護成員。例11.3在派生類中引用保護成員。#include<iostream>#include<string>usingnamespacestd;classStudent//聲明基類{public://基類公用成員voiddisplay〔〕;protected://基類保護成員intnum;stringname;charsex;};voidStudent::display〔〕//定義基類成員函數(shù){cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}classStudent1:protectedStudent//用protected方式聲明派生類Student1{public:voiddisplay1〔〕;//派生類公用成員函數(shù)private:intage;//派生類私有數(shù)據(jù)成員stringaddr;//派生類私有數(shù)據(jù)成員};voidStudent1::display1〔〕//定義派生類公用成員函數(shù){cout<<″num:″<<num<<endl;//引用基類的保護成員,合法cout<<″name:″<<name<<endl;//引用基類的保護成員,合法cout<<″sex:″<<sex<<endl;//引用基類的保護成員,合法cout<<″age:″<<age<<endl;//引用派生類的私有成員,合法cout<<″address:″<<addr<<endl;//引用派生類的私有成員,合法}intmain〔〕{Student1stud1;//stud1是派生類Student1類的對象stud1.display1〔〕;//合法,display1是派生類中的公用成員函數(shù)stud1.num=10023;//錯誤,外界不能訪問保護成員return0;}在派生類的成員函數(shù)中引用基類的保護成員是合法的。保護成員和私有成員不同之處,在于把保護成員的訪問范圍擴展到派生類中。注意:在程序中通過派生類Student1的對象stud1的公用成員函數(shù)display1去訪問基類的保護成員和sex,不要誤認為可以通過派生類對象名去訪問基類的保護成員。請補充、修改上面的程序,使之能正常運行。程序中應(yīng)包括輸入數(shù)據(jù)的函數(shù)。私有繼承和保護繼承方式在使用時需要十分小心,很容易搞錯,一般不常用。圖11.9如果有圖11.9所示的派生關(guān)系:類A為基類,類B是類A的派生類,類C是類B的派生類,那么類C也是類A的派生類。類B稱為類A的直接派生類,類C稱為類A的間接派生類。類A是類B的直接基類,是類C的間接基類。在多級派生的情況下,各成員的訪問屬性仍按以上原那么確定。11.4.4多級派生時的訪問屬性例11.4多級派生的訪問屬性。如果聲明了以下的類:classA//基類{public:inti;protected:voidf2〔〕;intj;private:intk;};classB:publicA//public方式{public:voidf3〔〕;protected:voidf4〔〕;private:intm;};classC:protectedB//protected方式{public:voidf5〔〕;private:intn;};類A是類B的公用基類,類B是類C的保護基類。各成員在不同類中的訪問屬性如下:

if2jkf3f4mf5n基類A公用保護保護私有

公用派生類B公用保護保護不可訪問

公用保護私有

保護派生類C保護保護保護不可訪問保護保護不可訪問

公用私有無論哪一種繼承方式,在派生類中是不能訪問基類的私有成員的,私有成員只能被本類的成員函數(shù)所訪問,畢竟派生類與基類不是同一個類。如果在多級派生時都采用公用繼承方式,那么直到最后一級派生類都能訪問基類的公用成員和保護成員。如果采用私有繼承方式,經(jīng)過假設(shè)干次派生之后,基類的所有的成員已經(jīng)變成不可訪問的了。如果采用保護繼承方式,在派生類外是無法訪問派生類中的任何成員的。而且經(jīng)過屢次派生后,人們很難清楚地記住哪些成員可以訪問,哪些成員不能訪問,很容易出錯。因此,在實際中,常用的是公用繼承。用戶在聲明類時可以不定義構(gòu)造函數(shù),系統(tǒng)會自動設(shè)置一個默認的構(gòu)造函數(shù),在定義類對象時會自動調(diào)用這個默認的構(gòu)造函數(shù)。這個構(gòu)造函數(shù)實際上是一個空函數(shù),不執(zhí)行任何操作。如果需要對類中的數(shù)據(jù)成員初始化,應(yīng)自己定義構(gòu)造函數(shù)。構(gòu)造函數(shù)的主要作用是對數(shù)據(jù)成員初始化。在設(shè)計派生類的構(gòu)造函數(shù)時,不僅要考慮派生類所增加的數(shù)據(jù)成員的初始化,還應(yīng)當考慮基類的數(shù)據(jù)成員初始化。也就是說,希望在執(zhí)行派生類的構(gòu)造函數(shù)時,使派生類的數(shù)據(jù)成員和基類的數(shù)據(jù)成員同時都被初始化。解決這個問題的思路是:在執(zhí)行派生類的構(gòu)造函數(shù)時,調(diào)用基類的構(gòu)造函數(shù)。11.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)任何派生類都包含基類的成員,簡單的派生類只有一個基類,而且只有一級派生〔只有直接派生類,沒有間接派生類〕,在派生類的數(shù)據(jù)成員中不包含基類的對象〔即子對象〕。例11.5簡單的派生類的構(gòu)造函數(shù)。#include<iostream>#include<string>usingnamespacestd;classStudent//聲明基類Student{public:Student〔intn,stringnam,chars〕//基類構(gòu)造函數(shù){num=n;name=nam;sex=s;}~Student〔〕{}//基類析構(gòu)函數(shù)11.5.1簡單的派生類的構(gòu)造函數(shù)protected://保護局部intnum;stringname;charsex;};classStudent1:publicStudent//聲明派生類Student1{public://派生類的公用局部Student1〔intn,stringnam,chars,inta,stringad〕:Student〔n,nam,s〕//派生類構(gòu)造函數(shù){age=a;//在函數(shù)體中只對派生類新增的數(shù)據(jù)成員初始化addr=ad;}voidshow〔〕{cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;cout<<″age:″<<age<<endl;cout<<″address:″<<addr<<endl<<endl;}~Student1〔〕{}//派生類析構(gòu)函數(shù)private://派生類的私有局部intage;stringaddr;};intmain〔〕{Student1stud1〔10010,″Wang-li″,′f′,19,″115BeijingRoad,Shanghai″〕;Student1stud2〔10011,″Zhang-fun″,′m′,21,″213ShanghaiRoad,Beijing″〕;stud1.show〔〕;//輸出第一個學(xué)生的數(shù)據(jù)stud2.show〔〕;//輸出第二個學(xué)生的數(shù)據(jù)return0;}運行結(jié)果為num:10010name:Wang-lisex:faddress:115BeijingRoad,Shanghainum:10011name:Zhang-funsex:maddress:213ShanghaiRoad,Beijing請注意派生類構(gòu)造函數(shù)首行的寫法:Student1〔intn,stringnam,chars,inta,stringad〕:Student〔n,nam,s〕其一般形式為派生類構(gòu)造函數(shù)名〔總參數(shù)表列〕:基類構(gòu)造函數(shù)名〔參數(shù)表列〕{派生類中新增數(shù)據(jù)成員初始化語句}在main函數(shù)中,建立對象stud1時指定了5個實參。它們按順序傳遞給派生類構(gòu)造函數(shù)Student1的形參。然后,派生類構(gòu)造函數(shù)將前面3個傳遞給基類構(gòu)造函數(shù)的形參。見圖11.10。圖11.10通過Student〔n,nam,s〕把3個值再傳給基類構(gòu)造函數(shù)的形參,見圖11.11。Student〔 n, nam, s〕 ↓ ↓ ↓Student〔int n, stringnam,chars〕//這是基類構(gòu)造函數(shù)的首部圖11.11在上例中也可以將派生類構(gòu)造函數(shù)在類外面定義,而在類體中只寫該函數(shù)的聲明:Student1〔intn,stringnam,chars,inta,stringad〕;在類的外面定義派生類構(gòu)造函數(shù):Student1∷Student1〔intn,stringnam,chars,inta,stringad〕:Student〔n,nam,s〕{age=a;addr=ad;}請注意:在類中對派生類構(gòu)造函數(shù)作聲明時,不包括基類構(gòu)造函數(shù)名及其參數(shù)表列〔即Student〔n,nam,s〕〕。只在定義函數(shù)時才將它列出。在以上的例子中,調(diào)用基類構(gòu)造函數(shù)時的實參是從派生類構(gòu)造函數(shù)的總參數(shù)表中得到的,也可以不從派生類構(gòu)造函數(shù)的總參數(shù)表中傳遞過來,而直接使用常量或全局變量。例如,派生類構(gòu)造函數(shù)首行可以寫成以下形式:Student1〔stringnam,chars,inta,stringad〕:Student〔10010,nam,s〕即基類構(gòu)造函數(shù)3個實參中,有一個是常量10010,另外兩個從派生類構(gòu)造函數(shù)的總參數(shù)表傳遞過來。請回憶一下在第9章介紹過的構(gòu)造函數(shù)初始化表的例子:Box::Box〔inth,intw,intlen〕:height〔h〕,width〔w〕,length〔len〕{}它也有一個冒號,在冒號后面的是對數(shù)據(jù)成員的初始化表。實際上,本章介紹的在派生類構(gòu)造函數(shù)中對基類成員初始化,就是在第9章介紹的構(gòu)造函數(shù)初始化表。也就是說,不僅可以利用初始化表對構(gòu)造函數(shù)的數(shù)據(jù)成員初始化,而且可以利用初始化表調(diào)用派生類的基類構(gòu)造函數(shù),實現(xiàn)對基類數(shù)據(jù)成員的初始化。也可以在同一個構(gòu)造函數(shù)的定義中同時實現(xiàn)這兩種功能。例如,例11.5中派生類的基類構(gòu)造函數(shù)的定義采用了下面的形式:Student1〔intn,stringnam,chars,inta,stringad〕:Student〔n,nam,s〕{age=a;//在函數(shù)體中對派生類數(shù)據(jù)成員初始化addr=ad;}可以將對age和addr的初始化也用初始化表處理,將構(gòu)造函數(shù)改寫為以下形式:Student1〔intn,stringnam,chars,inta,stringad〕:Student〔n,nam,s〕,age〔a〕,addr〔ad〕{}這樣函數(shù)體為空,更顯得簡單和方便。在建立一個對象時,執(zhí)行構(gòu)造函數(shù)的順序是:①派生類構(gòu)造函數(shù)先調(diào)用基類構(gòu)造函數(shù);②再執(zhí)行派生類構(gòu)造函數(shù)本身〔即派生類構(gòu)造函數(shù)的函數(shù)體〕。對上例來說,先初始化num,name,sex,然后再初始化age和addr。在派生類對象釋放時,先執(zhí)行派生類析構(gòu)函數(shù)~Student1〔〕,再執(zhí)行其基類析構(gòu)函數(shù)~Student〔〕。類的數(shù)據(jù)成員中還可以包含類對象,如可以在聲明一個類時包含這樣的數(shù)據(jù)成員:Students1;//Student是已聲明的類名,s1是Student類的對象這時,s1就是類對象中的內(nèi)嵌對象,稱為子對象〔subobject〕,即對象中的對象。通過例子來說明問題。11.5.2有子對象的派生類的構(gòu)造函數(shù)例11.6包含子對象的派生類的構(gòu)造函數(shù)。為了簡化程序以易于閱讀,這里設(shè)基類Student的數(shù)據(jù)成員只有兩個,即num和name。#include<iostream>#include<string>usingnamespacestd;classStudent//聲明基類{public://公用局部Student〔intn,stringnam〕//基類構(gòu)造函數(shù),與例11.5相同{num=n;name=nam;}voiddisplay〔〕//成員函數(shù),輸出基類數(shù)據(jù)成員{cout<<″num:″<<num<<endl<<″name:″<<name<<endl;}protected://保護局部intnum;stringname;};classStudent1:publicStudent//聲明公用派生類Student1{public:Student1〔intn,stringnam,intn1,stringnam1,inta,stringad〕:Student〔n,nam〕,monitor〔n1,nam1〕//派生類構(gòu)造函數(shù){age=a;addr=ad;}voidshow〔〕{cout<<″Thisstudentis:″<<endl;display〔〕;//輸出num和namecout<<″age:″<<age<<endl;//輸出agecout<<″address:″<<addr<<endl<<endl;//輸出addr}voidshow_monitor〔〕//成員函數(shù),輸出子對象{cout<<endl<<″Classmonitoris:″<<endl;monitor.display〔〕;//調(diào)用基類成員函數(shù)}private://派生類的私有數(shù)據(jù)Studentmonitor;//定義子對象〔班長〕intage;stringaddr;};intmain〔〕{Student1stud1〔10010,″Wang-li″,10001,″Li-sun″,19,″115BeijingRoad,Shanghai″〕;stud1.show〔〕;//輸出學(xué)生的數(shù)據(jù)stud1.show_monitor〔〕;//輸出子對象的數(shù)據(jù)return0;}運行時的輸出如下:Thisstudentis:num:10010name:Wang-liage:19address:115BeijingRoad,ShanghaiClassmonitoris:num:10001name:Li-sun派生類構(gòu)造函數(shù)的任務(wù)應(yīng)該包括3個局部:〔1〕對基類數(shù)據(jù)成員初始化;〔2〕對子對象數(shù)據(jù)成員初始化;〔3〕對派生類數(shù)據(jù)成員初始化。程序中派生類構(gòu)造函數(shù)首部如下:Student1〔intn,stringnam,intn1,stringnam1,inta,stringad〕:Student〔n,nam〕,monitor〔n1,nam1〕在上面的構(gòu)造函數(shù)中有6個形參,前兩個作為基類構(gòu)造函數(shù)的參數(shù),第3、第4個作為子對象構(gòu)造函數(shù)的參數(shù),第5、第6個是用作派生類數(shù)據(jù)成員初始化的。見圖11.12。圖11.12歸納起來,定義派生類構(gòu)造函數(shù)的一般形式為派生類構(gòu)造函數(shù)名〔總參數(shù)表列〕:基類構(gòu)造函數(shù)名〔參數(shù)表列〕,子對象名〔參數(shù)表列〕{派生類中新增數(shù)成員據(jù)成員初始化語句}執(zhí)行派生類構(gòu)造函數(shù)的順序是:①調(diào)用基類構(gòu)造函數(shù),對基類數(shù)據(jù)成員初始化;②調(diào)用子對象構(gòu)造函數(shù),對子對象數(shù)據(jù)成員初始化;③再執(zhí)行派生類構(gòu)造函數(shù)本身,對派生類數(shù)據(jù)成員初始化。派生類構(gòu)造函數(shù)的總參數(shù)表列中的參數(shù),應(yīng)當包括基類構(gòu)造函數(shù)和子對象的參數(shù)表列中的參數(shù)?;悩?gòu)造函數(shù)和子對象的次序可以是任意的,如上面的派生類構(gòu)造函數(shù)首部可以寫成Student1〔intn,stringnam,intn1,stringnam1,inta,stringad〕:monitor〔n1,nam1〕,Student〔n,nam〕編譯系統(tǒng)是根據(jù)相同的參數(shù)名〔而不是根據(jù)參數(shù)的順序〕來確立它們的傳遞關(guān)系的。但是習慣上一般先寫基類構(gòu)造函數(shù)。如果有多個子對象,派生類構(gòu)造函數(shù)的寫法依此類推,應(yīng)列出每一個子對象名及其參數(shù)表列。一個類不僅可以派生出一個派生類,派生類還可以繼續(xù)派生,形成派生的層次結(jié)構(gòu)。在上面表達的根底上,不難寫出在多級派生情況下派生類的構(gòu)造函數(shù)。例11.7多級派生情況下派生類的構(gòu)造函數(shù)。#include<iostream>#include<string>usingnamespacestd;classStudent//聲明基類{public://公用局部Student〔intn,stringnam〕//基類構(gòu)造函數(shù){num=n;name=nam;}voiddisplay〔〕//輸出基類數(shù)據(jù)成員11.5.3多層派生時的構(gòu)造函數(shù){cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;}protected://保護局部intnum;//基類有兩個數(shù)據(jù)成員stringname;};classStudent1:publicStudent//聲明公用派生類Student1{public:Student1〔intn,charnam[10],inta〕:Student〔n,nam〕//派生類構(gòu)造函數(shù){age=a;}//在此處只對派生類新增的數(shù)據(jù)成員初始化voidshow〔〕//輸出num,name和age{display〔〕;//輸出num和namecout<<″age:″<<age<<endl;}private://派生類的私有數(shù)據(jù)intage;//增加一個數(shù)據(jù)成員};classStudent2:publicStudent1//聲明間接公用派生類Student2{public://下面是間接派生類構(gòu)造函數(shù)Student2〔intn,stringnam,inta,ints〕:Student1〔n,nam,a〕{score=s;}voidshow_all〔〕//輸出全部數(shù)據(jù)成員{show〔〕;//輸出num和namecout<<″score:″<<score<<endl;//輸出age}private:intscore;//增加一個數(shù)據(jù)成員};intmain〔〕{Student2stud〔10010,″Li″,17,89〕;stud.show_all〔〕;//輸出學(xué)生的全部數(shù)據(jù)return0;}運行時的輸出如下:num:10010name:Liage:17score:89其派生關(guān)系如圖11.13所示。圖11.13請注意基類和兩個派生類的構(gòu)造函數(shù)的寫法:基類的構(gòu)造函數(shù)首部:Student〔intn,stringnam〕派生類Student1的構(gòu)造函數(shù)首部:Student1〔intn,stringnam],inta〕:Student〔n,nam〕派生類Student2的構(gòu)造函數(shù)首部:Student2〔intn,stringnam,inta,ints〕:Student1〔n,nam,a〕在聲明Student2類對象時,調(diào)用Student2構(gòu)造函數(shù);在執(zhí)行Student2構(gòu)造函數(shù)時,先調(diào)用Student1構(gòu)造函數(shù);在執(zhí)行Student1構(gòu)造函數(shù)時,先調(diào)用基類Student構(gòu)造函數(shù)。初始化的順序是:①先初始化基類的數(shù)據(jù)成員num和name。②再初始化Student1的數(shù)據(jù)成員age。③最后再初始化Student2的數(shù)據(jù)成員score。在使用派生類構(gòu)造函數(shù)時,有以下特殊的形式:〔1〕當不需要對派生類新增的成員進行任何初始化操作時,派生類構(gòu)造函數(shù)的函數(shù)體可以為空,即構(gòu)造函數(shù)是空函數(shù),如例11.6程序中派生類Student1構(gòu)造函數(shù)可以改寫為Student1〔intn,strinnam,intn1,strinnam1〕:Student〔n,nam〕,

monitor〔n1,nam1〕{}此派生類構(gòu)造函數(shù)的作用只是為了將參數(shù)傳遞給基類構(gòu)造函數(shù)和子對象,并在執(zhí)行派生類構(gòu)造函數(shù)時調(diào)用基類構(gòu)造函數(shù)和子對象構(gòu)造函數(shù)。在實際工作中常見這種用法。11.5.4派生類構(gòu)造函數(shù)的特殊形式〔2〕如果在基類中沒有定義構(gòu)造函數(shù),或定義了沒有參數(shù)的構(gòu)造函數(shù),那么在定義派生類構(gòu)造函數(shù)時可不寫基類構(gòu)造函數(shù)。因為此時派生類構(gòu)造函數(shù)沒有向基類構(gòu)造函數(shù)傳遞參數(shù)的任務(wù)。調(diào)用派生類構(gòu)造函數(shù)時系統(tǒng)會自動首先調(diào)用基類的默認構(gòu)造函數(shù)。如果在基類和子對象類型的聲明中都沒有定義帶參數(shù)的構(gòu)造函數(shù),而且也不需對派生類自己的數(shù)據(jù)成員初始化,那么可以不必顯式地定義派生類構(gòu)造函數(shù)。因為此時派生類構(gòu)造函數(shù)既沒有向基類構(gòu)造函數(shù)和子對象構(gòu)造函數(shù)傳遞參數(shù)的任務(wù),也沒有對派生類數(shù)據(jù)成員初始化的任務(wù)。在建立派生類對象時,系統(tǒng)會自動調(diào)用系統(tǒng)提供的派生類的默認構(gòu)造函數(shù),并在執(zhí)行派生類默認構(gòu)造函數(shù)的過程中,調(diào)用基類的默認構(gòu)造函數(shù)和子對象類型默認構(gòu)造函數(shù)。如果在基類或子對象類型的聲明中定義了帶參數(shù)的構(gòu)造函數(shù),那么就必須顯式地定義派生類構(gòu)造函數(shù),并在派生類構(gòu)造函數(shù)中寫出基類或子對象類型的構(gòu)造函數(shù)及其參數(shù)表。如果在基類中既定義無參的構(gòu)造函數(shù),又定義了有參的構(gòu)造函數(shù)〔構(gòu)造函數(shù)重載〕,那么在定義派生類構(gòu)造函數(shù)時,既可以包含基類構(gòu)造函數(shù)及其參數(shù),也可以不包含基類構(gòu)造函數(shù)。在調(diào)用派生類構(gòu)造函數(shù)時,根據(jù)構(gòu)造函數(shù)的內(nèi)容決定調(diào)用基類的有參的構(gòu)造函數(shù)還是無參的構(gòu)造函數(shù)。編程者可以根據(jù)派生類的需要決定采用哪一種方式。在派生時,派生類是不能繼承基類的析構(gòu)函數(shù)的,也需要通過派生類的析構(gòu)函數(shù)去調(diào)用基類的析構(gòu)函數(shù)。在派生類中可以根據(jù)需要定義自己的析構(gòu)函數(shù),用來對派生類中所增加的成員進行清理工作?;惖那謇砉ぷ魅匀挥苫惖奈鰳?gòu)函數(shù)負責。在執(zhí)行派生類的析構(gòu)函數(shù)時,系統(tǒng)會自動調(diào)用基類的析構(gòu)函數(shù)和子對象的析構(gòu)函數(shù),對基類和子對象進行清理。調(diào)用的順序與構(gòu)造函數(shù)正好相反:先執(zhí)行派生類自己的析構(gòu)函數(shù),對派生類新增加的成員進行清理,然后調(diào)用子對象的析構(gòu)函數(shù),對子對象進行清理,最后調(diào)用基類的析構(gòu)函數(shù),對基類進行清理。11.5.5派生類的析構(gòu)函數(shù)前面討論的是單繼承,即一個類是從一個基類派生而來的。實際上,常常有這樣的情況:一個派生類有兩個或多個基類,派生類從兩個或多個基類中繼承所需的屬性。C++為了適應(yīng)這種情況,允許一個派生類同時繼承多個基類。這種行為稱為多重繼承〔multipleinheritance〕。11.6多重繼承如果已聲明了類A、類B和類C,可以聲明多重繼承的派生類D:classD:publicA,privateB,protectedC{類D新增加的成員}D是多重繼承的派生類,它以公用繼承方式繼承A類,以私有繼承方式繼承B類,以保護繼承方式繼承C類。D按不同的繼承方式的規(guī)那么繼承A,B,C的屬性,確定各基類的成員在派生類中的訪問權(quán)限。11.6.1聲明多重繼承的方法多重繼承派生類的構(gòu)造函數(shù)形式與單繼承時的構(gòu)造函數(shù)形式根本相同,只是在初始表中包含多個基類構(gòu)造函數(shù)。如派生類構(gòu)造函數(shù)名〔總參數(shù)表列〕:基類1構(gòu)造函數(shù)〔參數(shù)表列〕,基類2構(gòu)造函數(shù)〔參數(shù)表列〕,基類3構(gòu)造函數(shù)〔參數(shù)表列〕{派生類中新增數(shù)成員據(jù)成員初始化語句}各基類的排列順序任意。派生類構(gòu)造函數(shù)的執(zhí)行順序同樣為:先調(diào)用基類的構(gòu)造函數(shù),再執(zhí)行派生類構(gòu)造函數(shù)的函數(shù)體。調(diào)用基類構(gòu)造函數(shù)的順序是按照聲明派生類時基類出現(xiàn)的順序。11.6.2多重繼承派生類的構(gòu)造函數(shù)例11.8聲明一個教師〔Teacher〕類和一個學(xué)生〔Student〕類,用多重繼承的方式聲明一個研究生〔Graduate〕派生類。教師類中包括數(shù)據(jù)成員name〔姓名〕、age〔年齡〕、title〔職稱〕。學(xué)生類中包括數(shù)據(jù)成員name1〔姓名〕、age〔性別〕、score〔成績〕。在定義派生類對象時給出初始化的數(shù)據(jù),然后輸出這些數(shù)據(jù)。#include<iostream>#include<string>usingnamespacestd;classTeacher//聲明類Teacher〔教師〕{public://公用局部Teacher〔stringnam,inta,stringt〕//構(gòu)造函數(shù){name=nam;age=a;title=t;}voiddisplay〔〕//輸出教師有關(guān)數(shù)據(jù){cout<<″name:″<<name<<endl;cout<<″age″<<age<<endl;cout<<″title:″<<title<<endl;}protected://保護局部stringname;intage;stringtitle;//職稱};classStudent//定義類Student〔學(xué)生〕{public:Student〔charnam[],chars,floatsco〕{strcpy〔name1,nam〕;sex=s;score=sco;}//構(gòu)造函數(shù)voiddisplay1〔〕//輸出學(xué)生有關(guān)數(shù)據(jù){cout<<″name:″<<name1<<endl;cout<<″sex:″<<sex<<endl;cout<<″score:″<<score<<endl;}protected://保護局部stringname1;charsex;floatscore;//成績};classGraduate:publicTeacher,publicStudent//聲明多重繼承的派生類Graduate{public:Graduate〔stringnam,inta,chars,stringt,floatsco,floatw〕:Teacher〔nam,a,t〕,Student〔nam,s,sco〕,wage〔w〕{}voidshow〔〕//輸出研究生的有關(guān)數(shù)據(jù){cout<<″name:″<<name<<endl;cout<<″age:″<<age<<endl;cout<<″sex:″<<sex<<endl;cout<<″score:″<<score<<endl;cout<<″title:″<<title<<endl;cout<<″wages:″<<wage<<endl;}private:floatwage;//工資};intmain〔〕{Graduategrad1〔″Wang-li″,24,′f′,″assistant″,89.5,1234.5〕;grad1.show〔〕;return0;}程序運行結(jié)果如下:name:Wang-liage:24sex:fscore:89.5title:assistancewages:1234.5在兩個基類中分別用name和name1來代表姓名,其實這是同一個人的名字,從Graduate類的構(gòu)造函數(shù)中可以看到總參數(shù)表中的參數(shù)nam分別傳遞給兩個基類的構(gòu)造函數(shù),作為基類構(gòu)造函數(shù)的實參。解決這個問題有一個好方法:在兩個基類中可以都使用同一個數(shù)據(jù)成員名name,而在show函數(shù)中引用數(shù)據(jù)成員時指明其作用域,如cout<<″name:″<<Teacher::name<<endl;這就是惟一的,不致引起二義性,能通過編譯,正常運行。通過這個程序還可以發(fā)現(xiàn)一個問題:在多重繼承時,從不同的基類中會繼承一些重復(fù)的數(shù)據(jù)。如果有多個基類,問題會更突出。在設(shè)計派生類時要細致考慮其數(shù)據(jù)成員,盡量減少數(shù)據(jù)冗余。多重繼承可以反映現(xiàn)實生活中的情況,能夠有效地處理一些較復(fù)雜的問題,使編寫程序具有靈活性,但是多重繼承也引起了一些值得注意的問題,它增加了程序的復(fù)雜度,使程序的編寫和維護變得相對困難,容易出錯。其中最常見的問題就是繼承的成員同名而產(chǎn)生的二義性〔ambiguous〕問題。在上一節(jié)中已經(jīng)初步地接觸到這個問題了?,F(xiàn)在作進一步的討論。如果類A和類B中都有成員函數(shù)display和數(shù)據(jù)成員a,類C是類A和類B的直接派生類。分別討論以下3種情況:11.6.3多重繼承引起的二義性問題〔1〕兩個基類有同名成員。如圖11.14所示。圖11.14classA{public:inta;voiddisplay〔〕;};classB{public:inta;voiddisplay〔〕;};classC:publicA,publicB{public:intb;voidshow〔〕;};如果在main函數(shù)中定義C類對象c1,并調(diào)用數(shù)據(jù)成員a和成員函數(shù)display:Cc1;c1.a=3;c1.display〔〕;由于基類A和基類B都有數(shù)據(jù)成員a和成員函數(shù)display,編譯系統(tǒng)無法判別要訪問的是哪一基類的成員,因此,程序編譯出錯。可以用基類名來限定:c1.A::a=3;//引用c1對象中的基類A的數(shù)據(jù)成員ac1.A::display〔〕;//調(diào)用c1對象中的基類A的成員函數(shù)display如果是在派生類C中通過派生類成員函數(shù)show訪問基類A的display和a,可以不必寫對象名而直接寫A::a=3;//指當前對象A::display〔〕;如同上一節(jié)最后所介紹的那樣。為清楚起見,圖11.14應(yīng)改用圖11.15的形式表示。圖11.15〔2〕兩個基類和派生類三者都有同名成員。將上面的C類聲明改為classC:publicA,publicB{inta;voiddisplay〔〕;};如圖11.16所示。即有3個a,3個display函數(shù)。圖11.16如果在main函數(shù)中定義C類對象c1,并調(diào)用數(shù)據(jù)成員a和成員函數(shù)display:Cc1;c1.a=3;c1.display〔〕;程序能通過編譯,也可正常運行。訪問的是派生類C中的成員。規(guī)那么是:基類的同名成員在派生類中被屏蔽,成為“不可見”的,或者說,派生類新增加的同名成員覆蓋了基類中的同名成員。因此如果在定義派生類對象的模塊中通過對象名訪問同名的成員,那么訪問的是派生類的成員。請注意:不同的成員函數(shù),只有在函數(shù)名和參數(shù)個數(shù)相同、類型相匹配的情況下才發(fā)生同名覆蓋,如果只有函數(shù)名相同而參數(shù)不同,不會發(fā)生同名覆蓋,而屬于函數(shù)重載。要在派生類外訪問基類A中的成員,應(yīng)指明作用域A,寫成以下形式:c1.A::a=3;//表示是派生類對象c1中的基類A中的數(shù)據(jù)成員ac1.A::display〔〕;//表示是派生類對象c1中的基類A中的成員函數(shù)display〔3〕如果類A和類B是從同一個基類派生的,如圖11.18所示。圖11.18classN{public:inta;voiddisplay〔〕{cout<<″A::a=”<<a<<endl;}};classA:publicN{public:inta1;};classB:publicN{public:inta2;};classC:publicA,publicB{public:inta3;voidshow〔〕{cout<<″a3=″<<a3<<endl;}};intmain〔〕{Cc1;//定義C類對象c1┆}圖11.19和圖11.20表示了派生類C中成員的情況。圖11.19圖11.20怎樣才能訪問類A中從基類N繼承下來的成員呢?顯然不能用c1.a=3;c1.display〔〕;或c1.N::a=3;c1.N::display〔〕;因為這樣依然無法區(qū)別是類A中從基類N繼承下來的成員,還是類B中從基類N繼承下來的成員。應(yīng)當通過類N的直接派生類名來指出要訪問的是類N的哪一個派生類中的基類成員。如c1.A::a=3;c1.A::display〔〕;//要訪問的是類N的派生類A中的基類成員1.虛基類的作用從上面的介紹可知:如果一個派生類有多個直接基類,而這些直接基類又有一個共同的基類,那么在最終的派生類中會保存該間接共同基類數(shù)據(jù)成員的多份同名成員。如圖11.19和圖11.20所示。在引用這些同名的成員時,必須在派生類對象名后增加直接基類名,以防止產(chǎn)生二義性,使其惟一地標識一個成員,如c1.A::display〔〕。在一個類中保存間接共同基類的多份同名成員,這種現(xiàn)象是人們不希望出現(xiàn)的。C++提供虛基類〔virtualbaseclass〕的方法,使得在繼承間接共同基類時只保存一份成員。11.6.4虛基類圖11.21圖11.22現(xiàn)在,將類A聲明為虛基類,方法如下:classA//聲明基類A{…};classB:virtualpublicA//聲明類B是類A的公用派生類,A是B的虛基類

{…};classC:virtualpublicA//聲明類C是類A的公用派生類,A是C的虛基類

{…};注意:虛基類并不是在聲明基類時聲明的,而是在聲明派生類時,指定繼承方式時聲明的。因為一個基類可以在生成一個派生類時作為虛基類,而在生成另一個派生類時不作為虛基類。聲明虛基類的一般形式為class派生類名:virtual繼承方式基類名經(jīng)過這樣的聲明后,當基類通過多條派生路徑被一個派生類繼承時,該派生類只繼承該基類一次。在派生類B和C中作了上面的虛基類聲明后,派生類D中的成員如圖11.23所示。需要注意:為了保證虛基類在派生類中只繼承一次,應(yīng)當在該基類的所有直接派生類中聲明為虛基類。否那么仍然會出現(xiàn)對基類的屢次繼承。如果像圖11.24所示的那樣,在派生類B和C中將類A聲明為虛基類,而在派生類D中沒有將類A聲明為虛基類,那么在派生類E中,雖然從類B和C路徑派生的局部只保存一份基類成員,但從類D路徑派生的局部還保存一份基類成員。圖11.23圖11.24D類intdata;intdata_b;intdata_cvoidfun();intdata_d;voidfun_d();2.虛基類的初始化如果在虛基類中定義了帶參數(shù)的構(gòu)造函數(shù),而且沒有定義默認構(gòu)造函數(shù),那么在其所有派生類〔包括直接派生或間接派生的派生類〕中,通過構(gòu)造函數(shù)的初始化表對虛基類進行初始化。例如classA//定義基類A{A〔inti〕{}//基類構(gòu)造函數(shù),有一個參數(shù)…};classB:virtualpublicA//A作為B的虛基類{B〔intn〕:A〔n〕{}//B類構(gòu)造函數(shù),在初始化表中對虛基類初始化…};classC:virtualpublicA//A作為C的虛基類{C〔intn〕:A〔n〕{}//C類構(gòu)造函數(shù),在初始化表中對虛基類初始化…};classD:publicB,publicC//類D的構(gòu)造函數(shù),在初始化表中對所有基類初始化{D〔intn〕:A〔n〕,B〔n〕,C〔n〕{}…};注意:在定義類D的構(gòu)造函數(shù)時,與以往使用的方法有所不同。規(guī)定:在最后的派生類中不僅要負責對其直接基類進行初始化,還要負責對虛基類初始化。C++編譯系統(tǒng)只執(zhí)行最后的派生類對虛基類的構(gòu)造函數(shù)的調(diào)用,而忽略虛基類的其他派生類〔如類B和類C〕對虛基類的構(gòu)造函數(shù)的調(diào)用,這就保證了虛基類的數(shù)據(jù)成員不會被屢次初始化。3.虛基類的簡單應(yīng)用舉例例11.9在例11.8的根底上,在Teacher類和Student類之上增加一個共同的基類Person,如圖11.25所示。作為人員的一些根本數(shù)據(jù)都放在Person中,在Teacher類和Student類中再增加一些必要的數(shù)據(jù)。圖11.25#include<iostream>#include<string>usingnamespacestd;//聲明公共基類PersonclassPerson{public:Person〔stringnam,chars,inta〕//構(gòu)造函數(shù){name=nam;sex=s;age=a;}protected://保護成員stringname;charsex;intage;};//聲明Person的直接派生類TeacherclassTeacher:virtualp

溫馨提示

  • 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

提交評論