吉大課件C++繼承與派生_第1頁
吉大課件C++繼承與派生_第2頁
吉大課件C++繼承與派生_第3頁
吉大課件C++繼承與派生_第4頁
吉大課件C++繼承與派生_第5頁
已閱讀5頁,還剩89頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

C++ProgrammingLanguageDr.ZhengXiaojuanAssociateProfessorSoftwareCollegeofNortheastNormalUniversityOctober.20081C++ProgrammingLanguage1

第七章繼承與派生

2

第七章繼承與派生2

本章內容

1繼承與派生2類的繼承方式3多繼承4派生類的構造和析構函數5派生中成員的標識與訪問虛基類基類私有成員的訪問引入派生類后的對象指針3本章內容1繼承與派生3在C++中,給我們提供了一種重要的機制,就是繼承。理解繼承是理解面向對象程序設計的關鍵。4在C++中,給我們提供了一種重要的機制,就是繼承。理解繼承#include<iostream.h>classAnimal{public:voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classfish{public:voideat();{cout<<"fisheat"<<endl;}voidsleep();{cout<<"fishsleep"<<endl;}voidbreathe(){cout<<"fishbreathe"<<endl;}};類的繼承再定義一個綿羊類?5#include<iostream.h>類的繼承再定義一個#include<iostream.h>classAnimal{public:voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{};voidmain(){ Animalan;Fishfh; an.eat(); fh.eat();類的繼承基類(父類)派生類(子類)派生類除了自己的成員變量和成員方法外,還可以繼承基類的成員變量和成員方法。6#include<iostream.h>類的繼承基類(父類1繼承與派生

1.1繼承與派生的概念

1.定義:繼承:保持已有類的特性而構造新類的過程。派生:在已有類的基礎上新增自己的特性而產生新類的過程。

2.可行性:對象具有自然相關性。

3.基類和派生類:被繼承特性的類稱為基類(或父類);新增特性從而派生出的類稱為派生類(或子類)。71繼承與派生1.1繼承與派生的概念7繼承與派生的目的繼承的目的:實現代碼重用。派生的目的:當新的問題出現,原有程序無法解決(或不能完全解決)時,需要對原有程序進行改造。8繼承與派生的目的繼承的目的:實現代碼重用。8

交通工具分類層次圖

根據事物的實際特征,抓住其共同特性和細小差別,利用分類的方法進行分析和描述反映了交通工具的派生關系由上到下,是一個具體化、特殊化的過程由下到上,是一個抽象化的過程上下層之間的關系就可以看作是基類與派生類的關系。9交通工具分類層次圖根據事物的實際特征,抓住其共同特性和

1.2派生類的聲明

1.語法:class<派生類名>:[繼承方式]<基類名>{派生類成員聲明;

};繼承方式:private、public和protected,規定了如何訪問從基類繼承的成員。系統的默認值為私有繼承(private)。派生類成員:從基類繼承來的所有成員新增加的數據和函數成員——派生類不同于基類的關鍵所在.2.如何設計派生類成員:比較基類和派生類之間的相同和差異,相同點則加以繼承(不必再定義出);而對差異部分則加以擴充(新增、重載overload或者覆蓋override

),必須定義出。101.2派生類的聲明10

例,從基類vehicle(汽車)公有派生car(小汽車)類的聲明形式如下:classvehicle {private: intwheels; floatweight;public: voidinitvehicle(intin_wheels,floatin_weight); intget_wheels(); floatget_weight(); //...};11例,從基類vehicle(汽車)公有派生car(小汽車)類classcar:publicvehicle {private: //新增私有數據成員

intpassenger_load;public: //新增公有函數成員voidinitcar(intin_wheels,floatin_weight,intpeople=4);intget_passengers(); //...};12classcar:publicvehicle 12

1.3派生類生成過程派生新類過程三個步驟:吸收基類成員—代碼重用的過程改造基類成員添加新的成員1.吸收基類成員派生類包含了它的所有基類中除構造和析構函數之外的所有成員。2.改造基類成員(1)對基類成員的訪問控制---主要依靠派生類聲明時的繼承方式來控制。

原有代碼的擴充過程131.3派生類生成過程原有代碼的擴充過程13(2)對基類數據或函數成員的覆蓋---在派生類中聲明一個和基類數據或函數同名的成員,參數表也要相同,(參數不同的情況屬于重載),派生的新成員就覆蓋了外層同名成員。3.添加新的成員---是繼承與派生機制的核心,是保證派生類在功能上有所發展的關鍵。

14(2)對基類數據或函數成員的覆蓋---在派生類中聲明一個和基classemployee{protected: char*name; //姓名

intindividualEmpNo; //個人編號

intgrade; //級別

floataccumPay; //月薪總額

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

public: employee(); ~employee(); voidpay(); //計算月薪函數

voidpromote(int); //升級函數

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

};15classemployee15classtechnician:publicemployee{private: floathourlyRate; //每小時酬金

intworkHours; //當月工作時數public: technician(); //構造函數

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

voiddisplayStatus(); //顯示人員信息};繼承了基類employee中成員:name,individualEmpNo,grade,accumPay,employeeNo,pay(),promote(int),displayStatus()。pay()和displayStatus()函數覆蓋了基類employee中的同名函數。新添加的數據成員16classtechnician:publicemploy2類的繼承方式2.1類的繼承方式與類中成員的訪問屬性的不同:1.類中成員的訪問屬性:(1)public:不僅能被類中其它成員訪問,也能在類外被類的對象訪問。(2)private:只能被類中其它成員訪問,不能在類外被類的對象訪問。(3)protected:類同于private,其差別表現在繼承與派生時對派生類的影響不同。172類的繼承方式2.1類的繼承方式與類中成員的訪問屬性的不

2.類的繼承方式:

(1)public(公有)繼承(2)protected(保護)繼承(3)private(私有)繼承對于不同的繼承方式,會導致基類成員原來的訪問屬性在派生類中有所變化。182.類的繼承方式:18類的繼承訪問特性基類的訪問特性類的繼承特性子類的訪問特性PublicProtectedPrivatePublicPublicProtectedNoaccessPublicProtectedPrivateProtectedProtectedProtectedNoaccessPublicProtectedPrivatePrivatePrivatePrivateNoaccess19類的繼承訪問特性基類的訪問特性類的繼承特性子類的訪問特性P2.4應用要點:public和private繼承方式的區別:(1)public公有繼承方式揭示了父類與子類在概念上的相關性;子類應該是父類的特化,當描述具有泛化和特化概念的倆個實體時,應采用public繼承方式。(2)private私有繼承方式則主要著重于實現代碼重用,其主要目的是為了繼承父類的接口或父類的數據結構、某些成員函數等。在概念上不具有泛化與特化的子類型關系。如表單類Table從點Point類繼承,程序員的主要目的是要重用基類中的GetX()、GetY()等函數。此時應采用private繼承方式,而不宜使用public繼承方式。202.4應用要點:20classPoint{public:Point(intx,inty){PointX=x;PointY=y;}intGetX(){returnPointX;}intGetY(){returnPointY;}private:intPointX,PointY;};classCircle:

publicPoint{public:Circle(intX,intY,intR):Point(x,y){Radius=R;}//…其它成員定義private:intRadius;};

21classPoint21classTable:privatePoint{public:Table(intRow,intColumn):Point(Row,Column){}intGetRow(){returnGetX();}intGetColumn{returnGetY();}//…其它成員定義};22classTable:privatePoint222.protected保護型繼承是前兩種的折中。對建立其所在類對象的模塊來說,它與private成員的性質相同。對于其派生類來說,它與public成員的性質相同。既實現了數據隱藏,又方便繼承,實現代碼重用。232.protected保護型繼承是前兩種的折中。23基類模塊派生類模塊public,private,protected模塊H模塊V水平訪問垂直訪問24基類模塊派生類模塊public,private,protec例protected成員舉例classA{protected:intx;}intmain(){Aa;a.x=5;//錯誤}25例protected成員舉例classA{25classA{protected:intx;}classB:publicA{public:voidFunction();};voidB::Function(){x=5;//正確}2626classA{26262727

繼承與派生訪問屬性總結①不可訪問的成員:來源:基類私有成員訪問屬性:派生類、派生類對象都無法訪問,繼續派生,也無法訪問。②私有成員:來源:從基類繼承的成員(私有繼承)及新增的私有成員訪問屬性:派生類內部可以訪問,派生類對象無法訪問,繼續派生,無法訪問。③保護成員:來源:從基類繼承的成員(保護繼承)及新增的保護成員訪問屬性:派生類內部可以訪問,派生類對象無法訪問,繼續派生,在新的派生類中可能成為私有成員(私有繼承)或保護成員(公有繼承和保護繼承時)。④公有成員:來源:從基類繼承的成員(公有繼承)及新增的公有成員訪問屬性:派生類、派生類對象都可以訪問,繼續派生,在新的派生類中可能成為私有成員(私有繼承)或保護成員(保護繼承時)或公有成員(公有成員繼承時)28繼承與派生訪問屬性總結283多繼承

C++允許派生類以兩種形式繼承基類,即單一(簡單)繼承和多重繼承1.含義:單一繼承---派生類只從一個基類派生。多重繼承---派生類從多個基類派生。2.派生類的定義語法:

class<派生類名>:[繼承方式]基類名1,[繼承方式]基類名2,...,[繼承方式]基類名n{

派生類成員聲明;

};3.在多繼承時,C++并沒有限制基類的個數,但不能有相同的基類,classZ:publicX,publicY,publicX//非法:X出現兩次

{//...

};B1{b1}B2{b2}C{c1}293多繼承C++允許派生類以兩種形式繼承基類,即單例:

classBase1{ //...};classBase2{ //...};

classMultiDerived:publicBase1,privateBase2{public: MultiDerived(); ~MultiDerived(); //...};30例:30

3.類族類族:一個相互關聯的類的家族。直接基類:在類族中,直接參與派生出某類的基類;間接基類:基類的基類甚至更高層的基類。

313.類族31#include<iostream.h>classA{public:voidf1();protected:intj1;private:inti1;};classB:publicA{public:voidf2();protected:intj2;private:inti2;};classC:publicB{public:voidf3();};回答下列問題:1.派生類B中成員函數f2()能否訪問基類A中的成員:f1()、i1和j1呢?2.派生類B中對象b1能否訪問基類A中的成員:f1()、i1和j1呢?3.派生類C中成員函數f3()能否訪問直接基類B中的成員:f2()和j2呢?能否訪問間接基類A中的成員:f1()、i1和j1呢?4.派生類C中對象c1能否訪問直接基類B中的成員:f2()、i2和j2呢?能否訪問間接基類A中的成員:f1()、i1和j1呢?32#include<iostream.h>classC:pu#include<iostream.h>classA{public:voidf1();protected:intj1;private:inti1;};classB:privateA{public:voidf2();protected:intj2;private:inti2;};classC:privateB{public:voidf3();};回答下列問題:1.派生類B中成員函數f2()能否訪問基類A中的成員:f1()、i1和j1呢?2.派生類B中對象b1能否訪問基類A中的成員:f1()、i1和j1呢?3.派生類C中成員函數f3()能否訪問直接基類B中的成員:f2()和j2呢?能否訪問間接基類A中的成員:f1()、i1和j1呢?4.派生類C中對象c1能否訪問直接基類B中的成員:f2()、i2和j2呢?能否訪問間接基類A中的成員:f1()、i1和j1呢?33#include<iostream.h>classC:pclassC:protectedB{public:voidf3();};回答下列問題:1.派生類B中成員函數f2()能否訪問基類A中的成員:f1()、i1和j1呢?2.派生類B中對象b1能否訪問基類A中的成員:f1()、i1和j1呢?3.派生類C中成員函數f3()能否訪問直接基類B中的成員:f2()和j2呢?能否訪問間接基類A中的成員:f1()、i1和j1呢?4.派生類C中對象c1能否訪問直接基類B中的成員:f2()、i2和j2呢?能否訪問間接基類A中的成員:f1()、i1和j1呢?#include<iostream.h>classA{public:voidf1();protected:intj1;private:inti1;};classB:protectedA{public:voidf2();protected:intj2;private:inti2;};34classC:protectedB#include<i4派生類的構造和析構函數派生類沒有繼承基類的構造函數,必須自行定義出。1.規則:構造函數必須包含對直接基類的構造函數的調用。2.形式:(1)單一繼承時<派生類名>::<派生類名>(基類形參定義,派生類自己的形參定義):基類的構造函數名(形參名){

派生類成員的初始化語句;}354派生類的構造和析構函數派生類沒有繼

(2)多重繼承時:

<派生類名>::<派生類名>(基類1的形參定義,…基類n的形參定義,派生類自己的形參定義):基類名1(形參名1),...,基類名n(形參名n){

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

}36(2)多重繼承時:36

(2)多重繼承并且帶有對象數據成員時:

<派生類名>::<派生類名>(基類1的形參定義,…基類n的形參定義,對象數據成員所需參數定義,派生類自己的形參定義):基類名1(參數表1),...,基類名n(參數表n),內嵌對象名1(內嵌對象參數表1),...,內嵌對象名m(內嵌對象參數表m){

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

}37(2)多重繼承并且帶有對象數據成員時:37

注意:(1)對于使用默認構造函數的基類,可以在派生類的構造函數中不給出基類構造函數名。(2)對于對象成員,如果是使用默認構造函數,也不需要寫出對象名和參數表。(3)派生類構造函數的執行順序一般是,先祖先(基類),再客人(內嵌對象),后自己(派生類本身)。

(4)派生類沒有定義構造函數,必須保證基類有缺省參數的構造函數。38注意:38

#include<iostream.h>classvehicle {private:

intwheels;floatweight;public: vehicle(intin_wheels,floatin_weight) {wheels=in_wheels;weight=in_weight;} //...};39#include<iostream.h>39classcar:publicvehicle {private: //新增私有數據成員

intpassenger_load;public: //派生類car的構造函數

car(intin_wheels,floatin_weight,intpeople=4):vehicle(in_wheels,in_weight) {passenger_load=people;} //...};voidmain(){ carbluebird(4,3); //聲明派生類car的對象//...}40classcar:publicvehicle 40#include<iostream.h>classAnimal{public:Animal(){cout<<"animalconstruct"<<endl;}

voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish(){cout<<"Fishconstruct"<<endl;}

};voidmain(){ Fishfh;}

animalconstructFishconstruct沒有父親就沒有孩子缺省參數的構造函數。41#include<iostream.h>animalco#include<iostream.h>classAnimal{public://Animal()//{cout<<"animalconstruct"<<endl;}

voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish(){cout<<"Fishconstruct"<<endl;}

};voidmain(){ Fishfh;}

Fishconstruct缺省參數的構造函數。42#include<iostream.h>Fishcons#include<iostream.h>classAnimal{public://Animal()//{cout<<"animalconstruct"<<endl;}

voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public://Fish()//{cout<<"Fishconstruct"<<endl;}

};voidmain(){ Fishfh;}

warningC4101:'fh':unreferencedlocalvariable缺省參數的構造函數。43#include<iostream.h>warningC#include<iostream.h>classAnimal{public:Animal(){cout<<"animalconstruct"<<endl;}

voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public://Fish()//{cout<<"Fishconstruct"<<endl;}

};voidmain(){ Fishfh;}

animalconstruct缺省參數的構造函數。44#include<iostream.h>animalco在子類中調用父類的帶參數的構造函數#include<iostream.h>classAnimal{public:Animal(intheight,intweight){cout<<"animalconstruct"<<endl;}

~Animal(){cout<<"animaldestruct"<<endl;}voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish(){cout<<"Fishconstruct"<<endl;}

~Fish(){cout<<"Fishdestruct"<<endl;}

};voidmain(){ Fishfh;} noappropriatedefaultconstructoravailable45在子類中調用父類的帶參數的構造函數#include<ios在子類中調用父類的帶參數的構造函數#include<iostream.h>classAnimal{public:Animal(intheight,intweight){cout<<"animalconstruct"<<endl;}

~Animal(){cout<<"animaldestruct"<<endl;}voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish():Animal(300,400),a(1){cout<<"Fishconstruct"<<endl;}

~Fish(){cout<<"Fishdestruct"<<endl;}private:

constinta;};voidmain(){ Fishfh;} 顯式地去調用父類的帶參數的構造函數,類中的常量成員也采取此法初始化。46在子類中調用父類的帶參數的構造函數#include<ios拷貝構造函數若建立派生類對象時調用缺省拷貝構造函數,則編譯器將自動調用基類的缺省拷貝構造函數。若編寫派生類的拷貝構造函數,則需要為基類相應的拷貝構造函數傳遞參數。例如:C::C(C&c1):B(c1){…}47拷貝構造函數若建立派生類對象時調用缺省拷貝構造函數,則編譯器

3.

析構函數---派生類沒有繼承基類的析構函數,必須自行定義出。

(1)規則:只需要為本層次的類定義出析構函數,不需顯示調用(由系統隱式調用)直接基類的析構函數.(2)定義:類同于一般的析構函數的定義.(3)原因:因為一個類中只有一個析構函數(無重載形式),能由系統準確識別并且自動隱式調用,系統會自己調用基類及成員對象的析構函數來對基類及對象成員進行清理。它的執行順序和構造函數正好嚴格相反——先自己(派生類本身),再客人(內嵌對象),后祖先(基類)。483.析構函數---派生類沒有繼承基類的析構函數,必須4.應用要點:(1)構造函數的調用次序:先祖先(基類),再客人(內嵌對象),后自己(派生類本身)。而析構函數則相反,先自己(派生類本身),再客人(內嵌對象),后祖先(基類)。(2)派生類只需為它的直接基類定義構造函數的形參并調用它,而不需為它的上上基類(爺爺類)定義形參(區別虛基類構造函數的形參定義);(3)一旦基類中定義出構造函數和析構函數,派生類也必須定義出它們,否則全部采用缺省形式.(4)基類及內嵌對象構造函數的調用順序:

基類:按照派生類聲明時的順序.

內嵌對象:按照成員在類中聲明的順序,494.應用要點:49繼承的析構函數#include<iostream.h>classAnimal{public:

Animal(){cout<<"animalconstruct"<<endl;}

~Animal(){cout<<"animaldestruct"<<endl;}

voideat(){cout<<"animaleat"<<endl;}voidsleep(){cout<<"animalsleep"<<endl;}voidbreathe(){cout<<"aniamlbreathe"<<endl;}};classFish:publicAnimal{public:Fish(){cout<<"Fishconstruct"<<endl;}

~Fish(){cout<<"Fishdestruct"<<endl;}

};voidmain(){ Fishfh;}

animalconstructFishconstructFishdestructanimaldestruct50繼承的析構函數#include<iostream.h>an【例】派生類的構造函數和析構函數(多繼承,含有內嵌對象)。#include<iostream.h>classBase1 //基類Base1,構造函數有參數{public: Base1(inti){cout<<"constructingBase1"<<i<<endl;} ~Base1(){cout<<"destructingBase1"<<endl;} };classBase2 //基類Base2,構造函數有參數{public: Base2(intj){cout<<"constructingBase2"<<j<<endl;} ~Base2(){cout<<"destructingBase2"<<endl;} };51【例】派生類的構造函數和析構函數(多繼承,含有內嵌對象)。5classBase3 //基類Base3,構造函數無參數{public: Base3(){cout<<"constructingBase3"<<endl;} ~Base3(){cout<<"destructingBase3"<<endl;} };52classBase3 //基類Base3,構造函數無參數classDerive:publicBase2,publicBase1,publicBase3{private: Base1memberBase1; Base2memberBase2; Base3memberBase3;public: Derive(inta,intb,intc,intd):Base2(b),memberBase2(d),memberBase1(c),Base1(a){}};voidmain(){ Deriveobject(2,4,6,8);}constructingBase24constructingBase12constructingBase3constructingBase16constructingBase28constructingBase3destructingBase3destructingBase2destructingBase1destructingBase3destructingBase1destructingBase2

Base1Base2Base3Derive53classDerive:publicBase2,publ5派生類中成員的標識與訪問5.1訪問時的二義性:在多重繼承時,基類與派生類之間或基類之間出現同名時,將出現訪問時的二義性(不確定性)。產生場合1:基類與派生類之間或基類之間出現同名成員---采用虛函數或同名覆蓋來解決。同名覆蓋原則:派生類覆蓋基類中同名成員;未強行指明則為派生類同名成員;如訪問被同名覆蓋的同名基類成員,應使用基類名限定。產生場合2:派生類從多個基類派生,而這些基類又從同一個基類派生,則在訪問共同基類中的成員時,將產生二義性---采用虛基類來解決。545派生類中成員的標識與訪問5.1訪問時的二義性:在多重繼5.2作用域分辨符“::”1.作用:用來限定要訪問的成員歸屬哪個類:

<類名>::<成員名><類名>::<成員名>(參數表)

在類的派生層次結構中,基類的成員和派生類新增的成員都具有類作用域。二者的作用范圍不同,是相互包含的兩個層,派生類在內層。555.2作用域分辨符“::”55

【例】繼承中使用作用域分辨符。#include<iostream.h>classBase {public: intn;voidfun(){cout<<"ThisisBase,n="<<n<<endl;}};classDerive:publicBase {public: intn; //同名數據成員

voidfun(){cout<<"ThisisDerive,n="<<n<<endl;} //同名函數成員};56【例】繼承中使用作用域分辨符。56voidmain(){ Deriveobj; obj.n=1; //對象名.成員名標識

obj.fun(); //對象名.成員函數名標識

obj.Base::n=2; //作用域分辨符標識

obj.Base::fun(); //訪問Base基類成員}程序運行結果為ThisisDerive,n=1ThisisBase,n=257voidmain()程序運行結果為ThisisDeri

2.多繼承中作用域分辨符的使用

B{b}B1{b1}B2{b2}C從上一級基類繼承來的成員擁有相同的名稱。對這種類型的同名成員也要使用作用域分辨符來唯一標識,而且必須用直接基類來進行限定。bb1bb2cBBB1B2C582.多繼承中作用域分辨符的使用B{b}B1{b1}B2{b【例】多繼承中使用作用域分辨符。59【例】多繼承中使用作用域分辨符。59#include<iostream.h>classLevel1 {public: intn1; voidfun1(){cout<<"ThisisLevel1,n1="<<n1<<endl;}};classLevel21:publicLevel1 {public: intn21;};60#include<iostream.h>60classLevel22:publicLevel1 {public: intn22;};classLevel3:publicLevel21,publicLevel22{public: intn3; voidfun3(){cout<<"ThisisLevel3,n3="<<n3<<endl;}};61classLevel22:publicLevel1 61voidmain(){ Level3obj; obj.n3=1; obj.fun3(); obj.Level21::n1=2; //使用直接基類

obj.Level21::fun1(); //使用直接基類

obj.Level22::n1=3; //使用直接基類

obj.Level22::fun1(); //使用直接基類}程序運行結果為

ThisisLevel3,n3=1ThisisLevel1,n1=2ThisisLevel1,n1=362voidmain()程序運行結果為626虛基類

6.1虛基類:在繼承時以virtual加以修飾的基類。

1.定義語法:各個虛基類的說明位置無先后次序要求,每個virtual只對其后的基類名起作用。

class<派生類>:virtual[繼承方式]<基類名>2.作用:它主要用來解決多重繼承時可能發生的對同一基類繼承多次而產生二義性的問題,為最遠的派生類(規定將在建立對象時所指定的類稱為最遠的派生類)提供一份基類的成員而不重復產生多次拷貝。

3.如何判斷是否為虛基類的問題:從某一個起點出發,經過不同的途徑,最后又匯合在一起;此共同的起點(基類)應為虛基類。

636虛基類6.1虛基類:在繼承時以virtual加以修【例】虛基類。派生時聲明Level1為虛基類通過Level21和Level22兩條派生路徑繼承來的基類Level1中的成員n1和fun1()只有一份拷貝。

64【例】虛基類。派生時聲明Level1為虛基類通過Level2

b1b2bBB1B2Cbb1bb2cBBB1B2CB{b}B1{b1}B2{b2}C使用虛基類后65b1b2bBB1B2Cbb1bb2cBBB1B2CB{b}

#include<iostream.h>classLevel1{public: intn1; voidfun1(){cout<<"ThisisLevel1,n1="<<n1<<endl;}};classLevel21:virtualpublicLevel1 {public: intn21;};66#include<iostream.h>66classLevel22:virtualpublicLevel1 {public: intn22;};classLevel3:publicLevel21,publicLevel22 {public: intn3; voidfun3(){cout<<"ThisisLevel3,n3="<<n3<<endl;}};67classLevel22:virtualpublicL

voidmain(){ Level3obj; obj.n3=1; obj.fun3(); obj.n1=2; //采用虛基類后,直接使用

obj.fun1(); //“對象名.成員名”方式}68voidmain()68

6.2虛基類及其派生類的構造函數

1.特點:虛基類和一般基類的最大差別在于派生類的構造函數定義。

2.規則:最遠的派生類的構造函數不僅需要分別對它的直接基類初始化,也需要對共同基類(虛基類)初始化并且調用其構造函數;

3.C++規定,虛基類子對象是由最遠派生類的構造函數通過調用虛基類的構造函數進行初始化的。而派生類的基類中所列出對這個虛基類的構造函數調用在執行中被忽略,這樣便保證了對虛基類的子對象只初始化一次。696.2虛基類及其派生類的構造函數694.虛基類的初始化與一般的多繼承的初始化在語法上是一樣的,但構造函數的調用次序不同。①虛基類的構造函數在非虛基類之前調用。②若同一層次中包含多個虛基類,這些虛基類的構造函數按它們說明的次序調用。③若虛基類由非虛基類派生而來,則仍然先調用基類構造函數,再調用派生類的構造函數。

704.虛基類的初始化與一般的多繼承的初始化在語法上是一樣的,但【例】虛基類及其派生類的構造函數。#include<iostream.h>classLevel1 {public: intn1; Level1(intin_n1){n1=in_n1;cout<<"ThisisLevel1,n1="<<n1<<endl;}};classLevel21:virtualpublicLevel1 {public: intn21; Level21(inta):Level1(a){n21=a;cout<<"ThisisLevel21,n21="<<n21<<endl;}};71【例】虛基類及其派生類的構造函數。71

classLevel22:virtualpublicLevel1 {public: intn22; Level22(inta):Level1(a){n22=a;cout<<"ThisisLevel22,n22="<<n22<<endl;}};classLevel3:publicLevel21,publicLevel22{public: intn3; Level3(inta):Level1(a),Level21(a),Level22(a){n3=a;cout<<"ThisisLevel3,n3="<<n3<<endl;}};voidmain(){ Level3obj(3); } 程序運行結果為ThisisLevel1,n1=3ThisisLevel21,n21=3ThisisLevel22,n22=3ThisisLevel3,n3=372classLevel22:virtualpublic不管是私有派生還是公有派生,派生類都無權訪問基類的私有成員。派生類想要使用基類的私有成員,只能通過調用基類的成員函數來實現,也就是使用基類所提供的接口。對于需要頻繁訪問基類私有成員的派生類-------尋求直接訪問基類私有成員的方式。有兩種方式。1.在類定義體中增加保護段2.將需訪問基類私有成員的派生類成員函數聲明為基類的友元7基類私有成員的訪問73不管是私有派生還是公有派生,派生類都無權訪問基類的私有成

1.在類定義體中增加保護段為了便于派生類的訪問,可以將基類的私有成員中需提供給派生類訪問的部分定義為保護段成員。保護段成員可以被它的派生類訪問,但是對于外界是隱藏的。這樣,即方便了派生類的訪問,又禁止了外界對它的操作。

741.在類定義體中增加保護段74#include<iostream.h>classvehicle {protected: //保護數據成員

intwheels;floatweight;public: //公有函數成員

vehicle(intin_wheels,floatin_weight) {wheels=in_wheels;weight=in_weight;}intget_wheels(){returnwheels;}floatget_weight(){returnweight;}floatwheel_load(){returnweight/wheels;}voidprint();};【例】在類定義體中增加保護段直接訪問基類私有成員。75#include<iostream.h>【例】在類定義體中增classcar:vehicle {private: //新增私有數據成員

intpassenger_load;public: //新增公有函數成員

car(intin_wheels,floatin_weight,intpeople=4):vehicle(in_wheels,in_weight) {passenger_load=people;}intget_passengers(){returnpassenger_load;}voidprint();};vehicle{wheels,weight}car{passenger_load}truck{passenger_load,payload}76classcar:vehicle vehicle{wheeclasstruck:vehicle {private: //新增私有數據成員

intpassenger_load;floatpayload;public: //新增公有函數成員

truck(intin_wheels,floatin_weight,intpeople=2,floatmax_load=24000.00):vehicle(in_wheels,in_weight) {passenger_load=people;payload=max_load;}intget_passengers(){returnpassenger_load;}floatefficiency(){returnpayload/(payload+weight);}voidprint();};77classtruck:vehicle 77voidvehicle::print() //輸出汽車類vehicle的數據{cout<<"thewheelsofvehicleis"<<wheels<<endl;cout<<"theweightofvehicleis"<<weight<<endl;cout<<endl;}voidcar::print() {cout<<"thewheelsofcaris"<<wheels<<endl;cout<<"theweightofcaris"<<weight<<endl;cout<<"thepassenger_loadofcaris"<<passenger_load<<endl;cout<<endl;}78voidvehicle::print() //輸出汽車類vvoidtruck::print() //輸出卡車類truck的數據{cout<<"thewheelsoftruckis"<<wheels<<endl;cout<<"theweightoftruckis"<<weight<<endl;cout<<"thepassenger_loadoftruckis"<<passenger_load<<endl;cout<<"theefficencyoftruckis"<<efficiency()<<endl;cout<<endl;}79voidtruck::print() //輸出卡車類trvoidmain(){carbluebird(4,1000,5); truckdongfeng(10,5000,3,34000); bluebird.print();dongfeng.print();}程序運行結果為Thewheelsofcaris4Theweightofcaris1000Thepassenger_loadofcaris5Thewheelsoftruckis10Theweightoftruckis5000Thepassenger_loadoftruckis3Theefficiencyoftruckis0.87179580voidmain()程序運行結果為801.定義:任何被說明為指向基類對象的指針都可以指向它的公有派生類。【例】引入派生類后的對象指針。#include<iostream.h>#include<string.h>classstring{char*name;intlength;public:string(char*str){8引入派生類后的對象指針811.定義:任何被說明為指向基類對象的指針都可以指向它的公有派length=strlen(str);name=newchar[length+1];strcpy(name,str);}voidshow(){cout<<name<<endl;}};classde_string:publicstring{intage;public:de_string(char*str,intage):string(str){de_string::age=age;}voidshow(){string::show(); cout<<"theageis:"<<age<<endl;}};82length=strlen(str);82main(){strings1("Smith"),*ptr1;//定義string類對象s1及指針ptr1de_strings2("Jean",20),*ptr2;//定義de_string類對象s2及指針ptr2ptr1=&s1; //將ptr1指向s1對象ptr1->show(); //調用string類的成員函數ptr1=&s2;//將ptr1指向string類的派生類de_string的對象s2ptr1->show(); //調用s2對象所屬的基類的成員函數show()ptr2=&s2; //將ptr2指向de_string類對象s2ptr2

溫馨提示

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

評論

0/150

提交評論