




已閱讀5頁(yè),還剩81頁(yè)未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
從已有的對(duì)象類型出發(fā)建立一種新的對(duì)象類型,使它繼承原對(duì)象類型的特點(diǎn)和功能,這種思想是面向?qū)ο笤O(shè)計(jì)方法的主要貢獻(xiàn)。 通過(guò)對(duì)已有類進(jìn)行特殊化(派生)來(lái)建立新的數(shù)據(jù)類型,就使得面向?qū)ο笳Z(yǔ)言具有極大的能力和豐富的表現(xiàn)力。從概念上講,類的派生創(chuàng)建了一種軟件結(jié)構(gòu),它真實(shí)地反映了實(shí)際問(wèn)題。從軟件角度來(lái)看,類的派生創(chuàng)建了一種類族。派生類的對(duì)象也是基類的一種對(duì)象,它可以被用在基類對(duì)象所使用的任何地方??梢杂枚鄳B(tài)成員函數(shù)仔細(xì)調(diào)整這種關(guān)系,以便使派生類在某些地方與它的基類一致,而在別的地方表現(xiàn)出它自身的行為特征。 本章主要討論C+語(yǔ)言繼承方面的語(yǔ)法特征和一般的使用方法。,第6章 繼承和派生,主要內(nèi)容,6.1 繼承和派生的基本概念 6.2 單一繼承 6.3 多重繼承 6.4 二義性及其支配規(guī)則 6.5 典型問(wèn)題分析,交通工具分類層次圖的UML表示,交通工具,火車,汽車,飛機(jī),輪船,卡車,旅行車,小汽車,工具車,轎車,面包車,類的派生: 這種通過(guò)特殊化已有的類來(lái)建立新類的過(guò)程,叫做“類的派生”。從類的成員的角度看,派生類自動(dòng)地將基類的所有成員作為自己的成員,這叫做“繼承”。 基類、派生類: 原有的類叫做“基類”,新建立的類則叫做“派生類”。 基類和派生類又可以分別叫做“父類”和“子類”,有時(shí)也稱為“一般類”和“特殊類”。,類的派生和繼承是面向?qū)ο蟪绦蛟O(shè)計(jì)方法和C+語(yǔ)言最重要的特征之一。 繼承使得程序員可以在一個(gè)較一般的類的基礎(chǔ)上很快地建立一個(gè)新類,而不必從零開始設(shè)計(jì)每個(gè)類。 從一個(gè)或多個(gè)以前定義的類(基類) 產(chǎn)生新類的過(guò)程稱為派生,這個(gè)新類稱為派生類。派生的新類同時(shí)也可以增加或重新定義數(shù)據(jù)和操作,這就產(chǎn)生了類的層次性。 類的繼承是指新類繼承基類的成員。繼承常用來(lái)表示類屬關(guān)系,不能將繼承理解為構(gòu)成關(guān)系。,當(dāng)從現(xiàn)存類中派生出新類時(shí),可以對(duì)派生類做如下幾種變化: 可以增加新的數(shù)據(jù)成員; 可以增加新的成員函數(shù); 可以重新定義已有的成員函數(shù); 可以改變現(xiàn)有成員的屬性。,如圖6.1所示,C+中有兩種繼承:?jiǎn)我焕^承和多重繼承。對(duì)于單一繼承,派生類只能有一個(gè)基類;對(duì)于多重繼承,派生類可以有多個(gè)基類。,圖6.1 類的單一繼承和多重繼承的UML結(jié)構(gòu)圖,C+派生類從父類中繼承性質(zhì)時(shí),可使派生類擴(kuò)展它們,或者對(duì)其做些限制,也可改變或刪除,甚至不作任何修改。所有這些變化可歸結(jié)為兩類基本的面向?qū)ο蠹夹g(shù)。 第一種稱為性質(zhì)約束,即對(duì)基類的性質(zhì)加以限制或刪除。第二種稱為性質(zhì)擴(kuò)展,即增加派生類的性質(zhì)。,派生類的定義格式,單繼承的定義格式如下: Class : ; 多繼承的定義格式如下: Class : , , ; 常使用如下三種關(guān)鍵字給予表示: public 表示公有繼承 private 表示私有繼承 protected 表示保護(hù)繼承,派生類的三種繼承方式,公有繼承(public) 基類的公有成員和保護(hù)成員作為派生類的成員時(shí),它們都保持原有的狀態(tài),而基類的私有成員仍然是私有的 私有繼承(private) 基類的公有成員和保護(hù)成員都作為派生類的私有成員,并且不能被這個(gè)派生類的子類所訪問(wèn) 保護(hù)繼承(protected) 基類的所有公有成員和保護(hù)成員都作為派生類的保護(hù)成員,并且只能被它的派生類成員函數(shù)或友元訪問(wèn),基類的私有成員仍然是私有的 系統(tǒng)的默認(rèn)值是私有繼承(private)。,基類和派生類的關(guān)系,任何一個(gè)類都可以派生出一個(gè)新類,派生類也可以再派生出新類 類A是類C的間接基類, 類B是類A的直接派生類 基類與派生類之間的關(guān)系:可復(fù)用的軟件構(gòu)件 派生類是基類的具體化 派生類是基類定義的延續(xù) 派生類是基類的組合,6.2.1 單一繼承的一般形式 在 C+中,聲明單一繼承的一般形式為: class 派生類名:訪問(wèn)控制 基類名 private: 成員聲明列表 protected: 成員聲明列表 public: 成員聲明列表 ;,6.2 單一繼承,這里和一般的類的聲明一樣,用關(guān)鍵字class 聲明一個(gè)新的類。 冒號(hào)后面的部分指示這個(gè)新類是哪個(gè)基類的派生類。所謂“訪問(wèn)控制”是指如何控制基類成員在派生類中的訪問(wèn)屬性,它是3個(gè)關(guān)鍵字public, protected和private 中的一個(gè)。 一對(duì)大括號(hào)“ ”中是用來(lái)聲明派生類自己的成員的。這和類的聲明一樣,不再贅述。,單繼承,每一個(gè)類可以有多個(gè)派生類 每一個(gè)派生類只能有一個(gè)基類從而形成樹形結(jié)構(gòu),成員訪問(wèn)權(quán)限的控制,公有繼承public 私有繼承private 保護(hù)繼承protected,公有繼承(public),公有繼承方式創(chuàng)建的派生類對(duì)基類各種成員訪問(wèn)權(quán)限如下 : 基類公有成員相當(dāng)于派生類的公有成員,即派生類可以象訪問(wèn)自身公有成員一樣訪問(wèn)從基類繼承的公有成員。 基類保護(hù)成員相當(dāng)于派生類的保護(hù)成員,即派生類可以象訪問(wèn)自身的保護(hù)成員一樣,訪問(wèn)基類的保護(hù)成員。 對(duì)于基類的私有成員,派生類內(nèi)部成員無(wú)法直接訪問(wèn)。派生類使用者也無(wú)法通過(guò)派生類對(duì)象直接訪問(wèn)。,例 分析程序中的訪問(wèn)權(quán)限 #include class A public: void f 1( ); protected: int j1; private: int i1; ;,class B:public A public: void f2( ); protected: int j2; private: int i2; ; class C:public B public: void f3( ); ;,回答下列問(wèn)題: 派生類B中成員函數(shù)f2( )能否訪問(wèn)基類A中的成員:f1( ), i1 和 j1呢? 派生類B的對(duì)象b1能否訪問(wèn)基類A中的成員:f1( ), i1 和 j1呢? 派生類C中成員函數(shù)f3( )能否訪問(wèn)直接基類B中的成員:f2( )和j2呢?能否訪問(wèn)間接基類A中的成員:f1( ), i1 和 j1 呢? 派生類C的對(duì)象c1能否訪問(wèn)直接基類B中的成員:f2( )和j2呢?能否訪問(wèn)間接基類A中的成員:f1( ), i1和j1呢? 從對(duì)(1)(4)問(wèn)題的回答可得出什么結(jié)論?,Ans:,可以訪問(wèn)f1( )和j1,而不可以訪問(wèn)i1。 可以訪問(wèn)f1( ),而不可以訪問(wèn)j1和i1。 可以訪問(wèn)直接基類中的f2( )和j2以及間接基類中的f1( )和j1,而不可以訪問(wèn)i2和i1。 可以訪問(wèn)直接基類中的f2( )和間接基類中的f1( ),其它的都不可以訪問(wèn)。 在公有繼承時(shí),派生類的成員函數(shù)可訪問(wèn)基類中的公有成員和保護(hù)成員;派生類的對(duì)象僅可訪問(wèn)基類中的公有成員。,私有繼承 (private),派生類對(duì)基類各種成員訪問(wèn)權(quán)限如下 : 基類公有成員和保護(hù)成員都相當(dāng)于派生類的私有成員,派生類只能通過(guò)自身的函數(shù)成員訪問(wèn)他們 對(duì)于基類的私有成員,無(wú)論派生類內(nèi)部成員或派生類使用者都無(wú)法直接訪問(wèn)。,例分析程序,回答問(wèn)題,#include class A public: void f(int i)cout i endl: void g( ) cout “gn“; ; class B:A public: void h( ) cout “hn“; A:f; ;,void main( ) B d1; d1.f(6); d1.g( ); d1.h( ); ,回答下列問(wèn)題: 執(zhí)行該程序時(shí),哪個(gè)語(yǔ)句會(huì)出現(xiàn)編譯錯(cuò)?為什么? 去掉出錯(cuò)語(yǔ)句后,執(zhí)行該程序后輸出結(jié)果如何? 程序中派生類B是從基類A繼承來(lái)的,這種缺省繼承方式是哪種繼承方式? 派生類B中,A:f的含義是什么? 將派生類B的繼承改為公有繼承方式該程序輸出什么結(jié)果?,Ans:,1 d1.g( );語(yǔ)句出現(xiàn)編譯錯(cuò)誤,因?yàn)锽是以私有繼承方式繼承類A的,所以B類的對(duì)象不可訪問(wèn)A類的成員函數(shù)。 2 d1.g( );語(yǔ)句注釋后,執(zhí)行該程序輸出以下結(jié)果: 6 h 3 使用class關(guān)鍵字定義類時(shí),缺省的繼承方式是private。 4 A:f;是將基類中的公有成員說(shuō)明為派生類的公有成員。 5 將class B:A改為class B:public A以后,輸出如下: 6 g h,保護(hù)繼承(public),保護(hù)繼承方式創(chuàng)建的派生類對(duì)基類各種成員訪問(wèn)權(quán)限如下 : 基類的公有成員和保護(hù)成員都相當(dāng)于派生類的保護(hù)成員,派生類可以通過(guò)自身的成員函數(shù)或其子類的成員函數(shù)訪問(wèn)他們 對(duì)于基類的私有成員,無(wú)論派生類內(nèi)部成員或派生類使用者都無(wú)法直接訪問(wèn),例 分析程序,回答問(wèn)題,Include #include class A public: A(const char *nm)strcpy(name, nm); private: char name80; ; class B:public A public: B(const char *nm):A(nm); void PrintName( )const; ;,void B:PrintName( ) const cout “name“ name endl; void main( ) B b1(“wang li“); b1.PrintName( ); ,回答下列問(wèn)題:,執(zhí)行該程序?qū)?huì)出現(xiàn)什么編譯錯(cuò)? 對(duì)出現(xiàn)的編譯錯(cuò)如何在訪問(wèn)權(quán)限上進(jìn)行修改? 修改后使該程序通過(guò)編譯,執(zhí)行執(zhí)行該程序后輸出結(jié)果是什么?,Ans:,1 編譯時(shí)出錯(cuò)行是:cout“name:“nameendl; 錯(cuò)誤信息提示name是私有成員不能訪問(wèn)。 2 在類A中,將private改寫為protected。這樣就可以通過(guò)編譯。 派生類可訪問(wèn)基類的保護(hù)部分,并把它作為派生類的公有部分;但程序其他部分把name作為私有成員。例如在main中,不能運(yùn)行 strcpy(s1,) 3 執(zhí)行修改后的該程序輸出如下結(jié)果: wang li,class C:public B public: void f3( ); ;,例 分析程序中的訪問(wèn)權(quán)限 #include class A public: void f 1( ); protected: int j1; private: int i1; ; class B:public A public: void f2( ); protected: int j2; private: int i2; ;,公有繼承public 私有繼承private 保護(hù)繼承rotected,void main( ) B b1 b1.f1( ); b1.f2( ); ,6.2.2 構(gòu)造函數(shù)和析構(gòu)函數(shù),1. 構(gòu)造函數(shù) 派生類對(duì)象是由基類中說(shuō)明的數(shù)據(jù)成員和派生類中說(shuō)明的數(shù)據(jù)成員共同構(gòu)成 基類中說(shuō)明的數(shù)據(jù)成員和操作所構(gòu)成的封裝體稱為基類子對(duì)象 派生類的構(gòu)造函數(shù)必須通過(guò)調(diào)用基類的構(gòu)造函數(shù)類初始化基類子對(duì)象 在定義派生類的構(gòu)造函數(shù)時(shí)除了對(duì)自己的數(shù)據(jù)成員進(jìn)行初始化外,還必須負(fù)責(zé)調(diào)用基類構(gòu)造函數(shù)使基類的數(shù)據(jù)成員得以初始化。如果派生類中還有子對(duì)象時(shí),還應(yīng)包含對(duì)子對(duì)象初始化的構(gòu)造函數(shù)。,在派生類中繼承的基類成員的初始化,需要由派生類的構(gòu)造函數(shù)調(diào)用基類的構(gòu)造函數(shù)來(lái)完成,這和初始化對(duì)象成員有類似之處。 定義派生類的構(gòu)造函數(shù)的一般形式為: 派生類名 : 派生類名(參數(shù)表0) : 基類名(參數(shù)表) ./函數(shù)體 ,派生類構(gòu)造函數(shù),派生類構(gòu)造函數(shù)的格式如下: () : (),() ; 派生類構(gòu)造函數(shù)的調(diào)用順序如下: 基類的構(gòu)造函數(shù) 子對(duì)象類的構(gòu)造函數(shù)(如果有的話) 派生類構(gòu)造函數(shù),例,#include class A public: A( )a=0; cout“As default constructor called.n“; A(int i)a=i; cout“As constructor called.n“; A( )cout “As destructor called.n“; void Print( ) const cout a “,“; int Geta( )return a; private: int a; ;,class B:public A public: B( )b=0; cout “Bs default constructor called.n“; B(int i, int j, int k); B( )cout “Bs destrutor called.n“; void Print( ); private: int b; A aa; ; B:B(int i, int j, int k) : A(i),aa(j) b = k; cout “Bs constructor called.n“; ,void B:Print( ) A:Print( ); cout b “,“ aa.Geta( ) endl; void main( ) B bb2; bb0 =B(1,2,5); bb1 =B(3,4,7); for(int i=0; i2; i+) bbi.Print( ); ,Ans:,As default constructor called. As default constructor called. 構(gòu)造函數(shù) Bs default constructor called. As default constructor called. As default constructor called. 構(gòu)造函數(shù) Bs default constructor called. As constructor called. As constructor called. Bs constructor called. 賦值 Bs destructor called. As destructor called. As destructor called. As constructor called. As constructor called. Bs constructor called. 賦值 Bs destructor called. As destructor called. As destructor called. 1, 5, 2 3, 7, 4 Bs destructor called. As destructor called. As destructor called. Bs destructor called. As destructor called. As destructor called.,2. 析構(gòu)函數(shù),當(dāng)對(duì)象被刪除時(shí),派生類的析構(gòu)函數(shù)被執(zhí)行 由于析構(gòu)函數(shù)不能被繼承,因此在執(zhí)行派生類的析構(gòu)函數(shù)時(shí),基類的析構(gòu)函數(shù)也將被調(diào)用 先執(zhí)行派生類的析構(gòu)函數(shù),再執(zhí)行基類的析構(gòu)函數(shù),例,#include class M public: M( )m1 = m2 = 0; M(int i, int j)m1 = i; m2 = j; void print( )cout m1 “,“ m2 “,“: M( )cout “Ms destructor called.n“; private: int m1, m2; ;,class N:public M public: N( )n = 0; N(int i, int j, int k); void print( )M:print( ); cout n endl; N( )cout “Ns destructor called.n“; private: int n; ; N:N(int i, int j, int k) : M(i,j), n(k) void main( ) N n1(5,6,7), n2(-2,-3,-4); n1.print( ); n2.print( ); ,Ans:,5, 6, 7 -2, -3, -4, Ns destructor called. Ms destructor called. Ns destructor called. Ms destructor called.,如何初始化派生類的對(duì)象呢?當(dāng)然也應(yīng)在派生類中聲明一個(gè)與派生類同名的函數(shù)。假設(shè)從基類Point派生一個(gè)描述矩形的類Rectangle 。類Rectangle繼承Point類的兩個(gè)數(shù)據(jù)成員作為矩形的一個(gè)頂點(diǎn),再為Rectangle類增加兩個(gè)數(shù)據(jù)成員H 和W,分別描述它的高和寬。為類Point設(shè)計(jì)一個(gè)構(gòu)造函數(shù)Point(int,int)和顯示數(shù)據(jù)的函數(shù)Showxy。為Rectangle類也設(shè)計(jì)構(gòu)造函數(shù)Rectangle(int,int, int, int)和顯示函數(shù)Show。由此可見,派生類增加了兩個(gè)新的數(shù)據(jù)成員以及相應(yīng)的成員函數(shù),同時(shí)繼承Point的全部成員。 【例6.1】是它們的程序?qū)崿F(xiàn)。 【例6.1】使用默認(rèn)內(nèi)聯(lián)函數(shù)實(shí)現(xiàn)單一繼承。 #include using namespace std;,6.2.2 派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù),class Point private: int x,y; public: Point(int a, int b)x=a; y=b; cout“Point.“endl; void Showxy()cout“x=“x“,y=“yendl; Point()cout“Delete Point“endl; ; class Rectangle : public Point private: int H, W;,public: Rectangle(int a, int b, int h, int w):Point(a,b) /構(gòu)造函 /數(shù)初始化列表 H=h; W=w; cout“Rectangle.“endl; void Show()cout“H=“H“,W=“Wendl; Rectangle()cout“Delete Rectangle“endl; ; void main() Rectangle r1(3,4,5,6); r1.Showxy(); /派生類對(duì)象調(diào)用基類的成員函數(shù) r1.Show(); /派生類對(duì)象調(diào)用派生類的成員函數(shù) ,程序輸出如下: Point. /調(diào)用基類構(gòu)造函數(shù) Rectangle. /調(diào)用派生類構(gòu)造函數(shù) x=3,y=4 /調(diào)用基類成員函數(shù)Showxy() H=5,W=6 /調(diào)用派生類成員函數(shù)Show() Delete Rectangle /調(diào)用派生類析構(gòu)函數(shù) Delete Point /調(diào)用基類析構(gòu)函數(shù) 在派生類中繼承的基類成員的初始化,需要由派生類的構(gòu)造函數(shù)調(diào)用基類的構(gòu)造函數(shù)來(lái)完成,這和初始化對(duì)象成員有類似之處。 定義派生類的構(gòu)造函數(shù)的一般形式為: 派生類名 : 派生類名(參數(shù)表0) : 基類名(參數(shù)表) ./函數(shù)體 ,冒號(hào)后“基類名(參數(shù)表)”稱為成員初始化列表,參數(shù)表給出所調(diào)用的基類構(gòu)造函數(shù)所需要的實(shí)參。實(shí)參的值可以來(lái)自“參數(shù)表0”,或由表達(dá)式給出??梢韵馬ectangle那樣,在類中直接定義為內(nèi)聯(lián)函數(shù)。下面是在類說(shuō)明之外定義的示范: Rectangle:Rectangle(int a, int b, int h, int w):Point(a,b) H=h; W=w; cout“Rectangle.“endl; “參數(shù)表0”有4個(gè)參數(shù),基類Point的參數(shù)表是自己的2個(gè)數(shù)據(jù)成員。 構(gòu)造函數(shù)(包括析構(gòu)函數(shù))是不被繼承的,所以一個(gè)派生類只能調(diào)用它的直接基類的構(gòu)造函數(shù)。當(dāng)定義派生類的一個(gè)對(duì)象時(shí),首先調(diào)用基類的構(gòu)造函數(shù),對(duì)基類成員進(jìn)行初始化,然后執(zhí)行派生類的構(gòu)造函數(shù),如果某個(gè)基類仍是一個(gè)派生類,則這個(gè)過(guò)程遞歸進(jìn)行。當(dāng)該對(duì)象消失時(shí),析構(gòu)函數(shù)的執(zhí)行順序和執(zhí)行構(gòu)造函數(shù)時(shí)的順序正好相反。輸出結(jié)果也證實(shí)了這個(gè)結(jié)論。,現(xiàn)在修改Rectangle 的Show函數(shù),使得它可以一次顯示x 、y 、H 和W 。怎樣修改成員函數(shù)Show呢?修改成下面的內(nèi)容能實(shí)現(xiàn)這一目的嗎? void Rectangle:show() cout “x=“ x “,y=“y “,H=“ H “,W=“Wendl; 這段簡(jiǎn)單程序并不能通過(guò)編譯。類Rectangle有4個(gè)私有成員x 、y 、H 和W 。這4個(gè)私有成員的來(lái)源是不一樣的。H 和 W是Rectangle 自己定義的,而x 和y 是從Point那里繼承來(lái)的。 換句話說(shuō),x和y是類Point的私有成員。類的私有成員是只能被它自己的成員函數(shù)(不討論友元)訪問(wèn)的,而Show函數(shù)是在類Rectangle 中定義的,它是類Point子類的成員函數(shù),并不是類Point的成員函數(shù),因而不能訪問(wèn)x 和y 。,6.2.3 類的保護(hù)成員,C+語(yǔ)言規(guī)定,公有派生類的成員函數(shù)可直接訪問(wèn)基類中定義的或基類(從另一個(gè)基類)繼承來(lái)的公有成員,但不能訪問(wèn)基類的私有成員。這和私有成員的定義是一致的,符合數(shù)據(jù)封裝思想。但這樣也有問(wèn)題,就拿上面的程序來(lái)說(shuō),在類Rectangle 看來(lái),x 、y 、H 和W 的地位是平等的,現(xiàn)在希望對(duì)它們“一視同仁”,但C+語(yǔ)言關(guān)于私有成員繼承的規(guī)定卻妨礙這樣做。為解決這一矛盾,C+引入了保護(hù)成員的概念。 在類聲明中,關(guān)鍵字protected之后聲明的是類的保護(hù)成員。保護(hù)成員具有私有成員和公有成員的雙重角色:對(duì)派生類的成員函數(shù)而言,它是公有成員,可以被訪問(wèn);而對(duì)其他函數(shù)而言則仍是私有成員,不能被訪問(wèn)。因此,要想在類Rectangle 中使用統(tǒng)一的Show函數(shù),只要把x 和y 定義成類Point的保護(hù)成員就行了。【例6.2】是修改過(guò)的程序。,#include using namespace std; class Point protected: int x,y; public: Point(int a, int b)x=a; y=b; void Show()cout“x=”x“,y=”yendl; /基類的Show()函數(shù) ; class Rectangle : public Point private: int H, W; public: Rectangle(int, int, int, int); /構(gòu)造函數(shù)原型 void Show() cout“x=“x“,y=“y“,H=“H “,W=“Wendl; ;,【例6.2】演示使用protected成員。,/定義構(gòu)造函數(shù) Rectangle:Rectangle(int a, int b, int h, int w):Point(a,b) H=h; W=w; void main() Point a(3,4); Rectangle r1(3,4,5,6); a.Show(); /基類對(duì)象調(diào)用基類Show()函數(shù) r1.Show(); /派生類對(duì)象調(diào)用派生類Show()函數(shù) 程序還演示了在類體內(nèi)聲明Rectangle構(gòu)造函數(shù)原型,類體外定義它。派生類雖然繼承了基類的成員函數(shù)Show,但它改造了這個(gè)函數(shù),使它能顯示所有數(shù)據(jù)。這并不會(huì)影響基類函數(shù)原來(lái)的功能。程序有意定義了Point的對(duì)象a,執(zhí)行a.Show()驗(yàn)證之。 程序輸出如下: x=3,y=4 x=3,y=4,H=5,W=6 為了將來(lái)還可以派生這種新類,建議把H 和W 也定義成保護(hù)成員。然后就可以放心大膽地使用統(tǒng)一的Show函數(shù)了。,1. 公有派生和賦值兼容規(guī)則 在前面的例子中,使用了公有派生。在公有派生的情況下,基類成員的訪問(wèn)權(quán)限在派生類中保持不變。這就意味著在程序中: 基類的公有成員在派生類中仍然是公有的。 基類的保護(hù)成員在派生類中仍然是保護(hù)的。 基類的不可訪問(wèn)的和私有的成員在派生類中也仍然是不可訪問(wèn)的。 所謂“不可訪問(wèn)”,是說(shuō)一個(gè)成員甚至對(duì)于其自身所在類的成員來(lái)說(shuō)也是不可訪問(wèn)的。這似乎有點(diǎn)兒難以理解,但在類Rectangle 中已經(jīng)遇到過(guò)了,x 和y 就是不可訪問(wèn)的。在根類(不是從別的類派生出來(lái)的類)中,沒(méi)有成員是不可訪問(wèn)的。對(duì)于根類來(lái)說(shuō),可能的訪問(wèn)級(jí)別是 private 、public和protected 。但是在派生類中,可以存在第4種訪問(wèn)級(jí)別:不可訪問(wèn)(inaccessible)。,6.2.4 訪問(wèn)權(quán)限和賦值兼容規(guī)則,不可訪問(wèn)成員總是由基類繼承來(lái)的,要么是基類的不可訪問(wèn)成員,要么是基類的私有成員。因此,在公有派生的情況下,可以通過(guò)定義派生類自己的成員函數(shù)來(lái)訪問(wèn)派生類對(duì)象繼承來(lái)的公有和保護(hù)成員,但是不能訪問(wèn)繼承來(lái)的私有成員。這一點(diǎn)很重要,當(dāng)希望類的某些成員能夠被子類所訪問(wèn),而又不能被其他的外界函數(shù)訪問(wèn)的時(shí)候,就應(yīng)當(dāng)把它們定義為保護(hù)的,上一節(jié)就是這樣做的。千萬(wàn)不能把它們定義成私有的,否則在子類中它們就會(huì)是不可訪問(wèn)的。事實(shí)上,當(dāng)這樣做了以后,“每一只黑狗都是狗”,每一個(gè)派生類的對(duì)象,都是基類的一個(gè)對(duì)象,于是可以得出賦值兼容規(guī)則:所謂賦值兼容規(guī)則是指在公有派生情況下,一個(gè)派生類的對(duì)象可以作為基類的對(duì)象來(lái)使用的情況。 約定類derived 是從類base公有派生而來(lái)的,則指如下3種情況:,賦值兼容規(guī)則:所謂賦值兼容規(guī)則是指在公有派生情況下,一個(gè)派生類的對(duì)象可以作為基類的對(duì)象來(lái)使用的情況。 約定類derived 是從類base公有派生而來(lái)的,則指如下3種情況:, 派生的對(duì)象可以賦給基類的對(duì)象。例如: derived d; base b; b = d; 派生類的對(duì)象可以初始化基類的引用。例如: derived d; base 但要注意,在后兩種情況下,通過(guò)pb或br只能訪問(wèn)對(duì)象d 中所繼承的基類成員。下面使用【例6.2】定義的類,編寫一個(gè)主程序來(lái)演示賦值兼容規(guī)則并說(shuō)明這一問(wèn)題。,【例6.3】使用Point和Rectangle類演示賦值兼容規(guī)則的例子。 void main( ) /表演公有繼承的賦值兼容性規(guī)則 Point a(1,2); /基類對(duì)象a Rectangle b(3,4,5,6); /派生類對(duì)象b a.Show(); b.Show(); Point /派生類對(duì)象的地址賦給指向基類的指針,p-Show(); /實(shí)際調(diào)用的是基類的Show函數(shù) Rectangle* pb= 程序輸出如下: x=1,y=2 /用a的Show函數(shù)輸出對(duì)象a的數(shù)據(jù)成員 x=3,y=4,H=5,W=6 /用b的Show函數(shù)輸出對(duì)象b的 /數(shù)據(jù)成員 x=3,y=4 /用b的基類Show函數(shù)輸出其基類數(shù)據(jù)成員,x=3,y=4 /用b的基類Show函數(shù)輸出其基類數(shù)據(jù)成員 x=3,y=4,H=5,W=6 /b的指針調(diào)用Show函數(shù)輸出對(duì) /象b的數(shù)據(jù)成員 x=3,y=4 /用a的Show函數(shù)輸出對(duì)象a的數(shù)據(jù)成員 以指針為例,為什么“p-Show();”不是使用b的Show函數(shù)輸出“x=3,y=4,H=5,W=6”?因?yàn)椤癰ase* pb = ”,從輸出結(jié)果可以看出,它們的含義就是基類用派生類的屬性值代替自己原來(lái)的屬性值。這就是公有派生的“isa”原則。,2. “isa”和“ has-a”的區(qū)別 類與類之間的關(guān)系有兩大類。一是繼承和派生問(wèn)題,二是一個(gè)類使用另一個(gè)類的問(wèn)題。后者的簡(jiǎn)單用途是把另一個(gè)類的對(duì)象作為自己的數(shù)據(jù)成員或者成員函數(shù)的參數(shù)。 繼承首先要掌握公有繼承的賦值兼容規(guī)則,理解公有繼承“就是一個(gè)(isa)”的含義。如果寫成類B(子類 )公有繼承于類A(父類),在可以使用類A(父類)的對(duì)象的任何地方,則類B的對(duì)象同樣也能使用,因?yàn)槊恳粋€(gè)類B的對(duì)象“就是一個(gè)”類A的對(duì)象。另一方面,如果需要一個(gè)類B的對(duì)象,則類A的對(duì)象就不行:每個(gè)B都是A,但反之則不然。C+強(qiáng)制執(zhí)行公有繼承的這種規(guī)則。例如: class Person .; class Student : Public Person .; 這兩個(gè)類所斷言的是:每個(gè)學(xué)生都是人,但并不是每個(gè)人都是學(xué)生??梢云谕魏我患?duì)于人來(lái)說(shuō)是真實(shí)的事情,對(duì)學(xué)生也是真實(shí)的,例如他或她都有生日,對(duì)于學(xué)生同樣也是真實(shí)的。但卻不能期望每一件對(duì)于學(xué)生來(lái)說(shuō)是真實(shí)的,事情,對(duì)所有的人都是真實(shí)的。譬如說(shuō)他或她就讀于一所指定的學(xué)校,這對(duì)于一般的人就不能都是真實(shí)的?!叭恕钡母拍钜取皩W(xué)生”的概念來(lái)得更廣泛些;而“學(xué)生”則是一種特殊類型的“人”。 在C+范圍內(nèi),任何一個(gè)要求提供Person類型(或指向Person的指針及引用)參數(shù)的函數(shù),也能夠使用Student對(duì)象(或指向Student的指針及引用)作為參數(shù)。例如: void dance(const Person / 對(duì),s是Student,student也是(isa)Person,study(s); / 對(duì) study(p); / 錯(cuò)! p不都是Student 這只是對(duì)公有繼承才是正確的。僅當(dāng)Student類是從Person類中公有地派生出來(lái)的時(shí)候,C+才具有像上面所描述的那種性質(zhì)。 公共繼承和“isa”的等價(jià)性看起來(lái)很簡(jiǎn)單,但在實(shí)際應(yīng)用中并不容易。有時(shí)自己的直覺(jué)會(huì)產(chǎn)生誤導(dǎo)。譬如說(shuō),企鵝是鳥是一件事,鳥會(huì)飛是另一件事。企鵝是鳥,但不能從鳥會(huì)飛誤導(dǎo)出企鵝會(huì)飛。 如果已有一個(gè)address類,它描述地址這個(gè)概念,現(xiàn)在要建立一個(gè)worker類,它描述職工這個(gè)概念,每個(gè)職工都有一個(gè)住址,worker類可以有兩種定義形式:使用繼承或?qū)ο蟪蓡T。如果采用繼承,可以按如下形式定義:,class worker:public address / ; 如果在worker類中定義一個(gè)address類的類對(duì)象成員,可以按如下形式定義: class worker private address workerAddr; / ; 使用繼承的方法是說(shuō)職工是一個(gè)地址,使用對(duì)象的方法聲稱職工包含有一個(gè)地址屬性。這兩種說(shuō)法中后者的說(shuō)法是正確的,即地址只能作為職工的一個(gè)屬性,在概念上,它們之間沒(méi)有聯(lián)系,所以上例中第2種的描述形式是合理的。這就是分層實(shí)現(xiàn)。,由此可見,類用于描述一類對(duì)象的共同特性,不同種類的對(duì)象之間的聯(lián)系使用繼承來(lái)表示,對(duì)象所具有的屬性使用類的成員來(lái)表示。分層就是一種處理過(guò)程,它通過(guò)讓分層的類里包含被分層的類的對(duì)象作為其數(shù)據(jù)成員,以便把一個(gè)類建立在另一些類之上。例如: class String ; / 字符串 class Address ; / 某人居住的地方 class PhoneNumber; class Person private: String name; / 被分層的對(duì)象 Address address; / 被分層的對(duì)象,PhoneNumber voiceNumber; / 被分層的對(duì)象 PhoneNumber faxNumber; / 被分層的對(duì)象 public: ; 在這個(gè)例子中,Person類要被分層到String、Address和PhoneNumber這幾個(gè)類的上面,這是因?yàn)樗锩婧羞@些類型的數(shù)據(jù)成員。分層也可以叫做包含、嵌入或者聚合。公有繼承的意思是“isa”。與此相反,分層的意思是指“has-a(有一個(gè))”或者“is-implemented-in-terms-of(是按實(shí)現(xiàn)的)”。上面的Person類表示的是一種“has-a”的關(guān)系。一個(gè)Person對(duì)象有一個(gè)名字、一個(gè)地址和用于語(yǔ)音及傳真通信的兩個(gè)電話號(hào)碼。我們不會(huì)說(shuō)一個(gè)人就是一個(gè)字符串,或者一個(gè)人就是一個(gè)地址,但會(huì)說(shuō)一個(gè)人有一個(gè)字符串,并且有一個(gè)地址等。,許多人做這種分辨時(shí)困難不大,搞不清“isa”和“has-a”之間區(qū)別的人相對(duì)來(lái)說(shuō)也很少見。稍微有些傷腦筋的是“isa”和is-implemented-in-terms-of之間的差異,這可通過(guò)例子來(lái)加深理解。在6.5節(jié)中,將給出分別使用這兩種方法設(shè)計(jì)的例子。 3. 公有繼承存取權(quán)限表 派生類一般都使用公有繼承。使用基類的有基類本身、派生類、對(duì)象和外部函數(shù),對(duì)派生類而言,使用它的有派生類本身、對(duì)象和外部函數(shù)。 類中可以使用三種成員,表6.1總結(jié)了它們之間的關(guān)系。,由此可見,保護(hù)類型的成員(數(shù)據(jù)成員和成員函數(shù))介于私有和公有之間。對(duì)派生類來(lái)講,它的作用與public成員一樣。對(duì)類的對(duì)象、外部函數(shù)以及不屬于本類系之外的類來(lái)說(shuō),它與private成員一樣,均是不可訪問(wèn)的,從而保持了類的封裝性。 “訪問(wèn)控制”決定著基類各成員在派生類中的訪問(wèn)權(quán)限,上面討論了最常用的公有繼承方式,下面將討論private 和protected繼承方式。,4. 私有派生 通過(guò)私有派生,基類的私有和不可訪問(wèn)成員在派生類中是不可訪問(wèn)的,而公有和保護(hù)成員這時(shí)就成了派生類的私有成員,派生類的對(duì)象不能訪問(wèn)繼承的基類成員,必須定義公有的成員函數(shù)作為接口。更重要的是,雖然派生類的成員函數(shù)可通過(guò)自定義的函數(shù)訪問(wèn)基類的成員,但將該派生類作為基類再繼續(xù)派生時(shí),這時(shí)即使使用公有派生,原基類公有成員在新的派生類中也將是不可訪問(wèn)的。下面就來(lái)看一個(gè)私有派生的例子。 【例6.4】 私有派生的類繼續(xù)派生的例子。,#include using namespace std; class Point /基類定義 private: int x,y; public: Point(int a, int b)x=a; y=b; void Show()cout“x=“x“,y=“yendl; ; class Rectangle : private Point/派生類定義 private: / 新增私有數(shù)據(jù) int H, W; public: /新增外部接口 Rectangle(int a, int b, int h, int w):Point(a,b) H=h; W=w;,void Show()Point:Show(); cout“H=“H“,W=“Wendl; ; class Test: public Rectangle public: Test(int a, int b, int h, int w):Rectangle(a,b,h,w) voidShow()Rectangle:Show(); ;,在這個(gè)例子中,基類的公有成員函數(shù)Show通過(guò)私有派生成了派生類的私有成員函數(shù),Test雖然是公有派生,但它已經(jīng)無(wú)法使用基類的Show函數(shù),即不能通過(guò)“Point:Show();”方式使用Point類的Show函數(shù),這就徹底切斷了基類與外界的聯(lián)系。私有派生的這一特點(diǎn)不利于進(jìn)一步派生,因而實(shí)際中私有派生用得并不多。,5. 保護(hù)派生 派生也可以使用protected。這種派生使原來(lái)的權(quán)限都降一級(jí)使用:private變?yōu)椴豢稍L問(wèn);protected變?yōu)閜rivate;public變?yōu)閜rotected。因?yàn)橄拗屏藬?shù)據(jù)成員和成員函數(shù)的訪問(wèn)權(quán)限,所以用得較少。它與private繼承的區(qū)別主要在下一級(jí)的派生中。如果將上例Rectangle改為保護(hù)繼承方式,則在Test類中可以使用基類的Show函數(shù),則下面函數(shù)的定義是正確的: class Test: public Rectangle public: Test(int a, int b, int h, int w):Rectangle(a,b,h,w) void Show()Point:Show(); Rectangle:Show(); /可 /以使用基類Point的成員 ;,protected 成員舉例,int main() A a; a.x=5; /錯(cuò)誤! B b; b.x=5; /錯(cuò)誤! b. Function(); ,class A protected: int x; class B: public A public: void Function(); ; void B:Function() x=5; ,一個(gè)類從多個(gè)基類派生的一般形式是: class 類名1:訪問(wèn)控制 類名2, 訪問(wèn)控制 類名3 , ., 訪問(wèn)控制 類名n ./ 定義派生類自己的成員 ; 類名1繼承了類名2到類名n的所有數(shù)據(jù)成員和成員函數(shù),訪問(wèn)控制用于限制其后的類中的成員在類名1中的訪問(wèn)權(quán)限,其規(guī)則和單一繼承情況一樣。多重繼承可以視為是單一繼承的擴(kuò)展。 【例6.5】演示多重繼承的例子。,6.3 多重繼承,#include using namespace std; class A private: int a; public: void setA( int x )a=x; void showA( )cout “a=“ a endl; ; class B private: int b; public: void setB( int x ) b = x; void showB ( ) cou “b=“ b endl; ;,class C : public A, private B private: int c; public: void setC(int x, int y ) c=x; setB(y); void showC( ) showB( ); cout “c=“c endl; ; void main( ) C obj; obj.setA(53); obj.showA( ); /輸出a=53 obj.setC(55,58); obj.showC( ); /輸出b=58 c=55 ,類C從類A公有派生,因此,類A的公有成員(保護(hù)成員)在類C中仍是公有的(保護(hù)的)。類C從類B私有派生,類B的所有成員在類C中是私有的。這些成員在派生類中的可訪問(wèn)性和單一繼承中討論的一樣。 類B被私有繼承,因此,類C負(fù)責(zé)維護(hù)類B的數(shù)據(jù)成員值和顯示,所以在showC和setC中分別調(diào)用類B的成員函數(shù)showB和setB。使用obj.setB(5)和obj.showB( )都是錯(cuò)誤的。,6.4.1 二義性和作用域分辨符 對(duì)基類成員的訪問(wèn)必須是無(wú)二義性的,如使用一個(gè)表達(dá)式的含義能解釋為可以訪問(wèn)多個(gè)基類中的成員,則這種對(duì)基類成員的訪問(wèn)就是不確定的, 稱這種訪問(wèn)具有二義性。,6.4 二義性及其支配規(guī)則,C類的成員函數(shù)hunc訪問(wèn)func時(shí),無(wú)法確定是訪問(wèn)基類A還是基類B,出現(xiàn)二義性。使用A : func( )或B : func( )可以解決這種二義性。,【例6.6】 訪問(wèn)具有二義性的例子。 #include using namespace std; class A public: void func( ) cout“a.func“endl; ;,class B public: void func( ) cout“b.func“endl; Void gunc( ) cout“b.gunc“endl; ; class C : public A, public B public: void gunc( ) cout“c.gunc“endl; void hunc( )func(); / 具有二義性 ;,void main( ) C obj; obj.gunc(); / 不具有二義性,子類覆蓋父類 ,下面是正確的派生類C的實(shí)現(xiàn)方法: class C : public A, public B public: void gunc( )cout“c.gunc”endl; void hun1( )A:func(); / 使用基類A的func void hun2( )B:func(); / 使用基類B的func ; 不過(guò),程序仍然含有二義性,如用C類的對(duì)象obj訪問(wèn)函數(shù)func,則具有二義性: obj.func( ); / 不能確定是A的func還是B的func 使用成員名限定可以消除二義性,例如: obj.A : func( ); / A的func obj.B : func( ); / B的func obj.gunc( ); / C的gunc obj. B :gunc( ); / B的gunc,void main( ) C obj obj
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 山西體育職業(yè)學(xué)院《C語(yǔ)言》2023-2024學(xué)年第二學(xué)期期末試卷
- 貴陽(yáng)康養(yǎng)職業(yè)大學(xué)《微波測(cè)量技術(shù)及儀器》2023-2024學(xué)年第二學(xué)期期末試卷
- 寧波工程學(xué)院《成本會(huì)計(jì)學(xué)》2023-2024學(xué)年第二學(xué)期期末試卷
- 吉林城市職業(yè)技術(shù)學(xué)院《傳感與檢測(cè)技術(shù)》2023-2024學(xué)年第二學(xué)期期末試卷
- 九江職業(yè)技術(shù)學(xué)院《云計(jì)算和大數(shù)據(jù)技術(shù)》2023-2024學(xué)年第二學(xué)期期末試卷
- 南京城市職業(yè)學(xué)院《商務(wù)決策模型》2023-2024學(xué)年第二學(xué)期期末試卷
- 2024年發(fā)電機(jī)組、內(nèi)燃發(fā)電機(jī)組及旋轉(zhuǎn)式變流機(jī)項(xiàng)目資金需求報(bào)告代可行性研究報(bào)告
- 核磁共振成像設(shè)備維護(hù)保養(yǎng)培訓(xùn)
- 學(xué)生個(gè)人規(guī)劃課件
- 小學(xué)生感恩教育主題班會(huì)
- 《大學(xué)計(jì)算機(jī)基礎(chǔ)教程》課件第1章 計(jì)算機(jī)基礎(chǔ)知識(shí)
- 武漢版生命生態(tài)安全【武漢版】《生命安全教育》五年級(jí) 第7課《網(wǎng)絡(luò)資訊辨真假》課件
- 《電氣基礎(chǔ)知識(shí)培訓(xùn)》課件
- 中國(guó)共產(chǎn)主義青年團(tuán)團(tuán)章
- 游戲動(dòng)漫游戲體驗(yàn)提升及游戲衍生品開發(fā)策略
- 體育-小學(xué)移動(dòng)性技能:跳躍游戲教學(xué)設(shè)計(jì)與教案
- 第02輯一輪語(yǔ)法 專題16 with復(fù)合結(jié)構(gòu)(高考真題+名校模擬+寫作升格)(教師版) 2025屆新高三英語(yǔ)提分培優(yōu)通關(guān)練(高考真題+名校模擬)
- 核工業(yè)數(shù)字化轉(zhuǎn)型
- 京東MALL-盛大啟航消費(fèi)品開業(yè)慶典活動(dòng)策劃方案
- 動(dòng)漫衍生品拓展 游戲動(dòng)漫衍生品設(shè)計(jì)
- 計(jì)算機(jī)及外部設(shè)備裝配調(diào)試員理論考試復(fù)習(xí)題庫(kù)(含答案)
評(píng)論
0/150
提交評(píng)論