明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

繼承派生多態(tài)

[摘要]派生類派生類概述 利用繼承機(jī)制,新的類可以從已有的類中派生(有關(guān)繼承見下一節(jié)“單一繼承”的開始)。那些用于派生的類稱為這些特別派生出的類的“基類”。派生類的說明可以用下面的語法。 語法基類說明::基類表基類表:基類說明符基類表,基類說明符基類說明符:完全類名稱virtual 訪問說明符opt 完全...

派生類
派生類概述 
利用繼承機(jī)制,新的類可以從已有的類中派生(有關(guān)繼承見下一節(jié)“單一繼承”的開始)。那些用于派生的類稱為這些特別派生出的類的“基類”。派生類的說明可以用下面的語法。 
語法
基類說明::
基類表
基類表:
基類說明符
基類表,基類說明符
基類說明符:
完全類名稱
virtual 訪問說明符opt 完全類名稱 
訪問指示符 virtualopt 完全類名稱
訪問指示符:

private

protected

public
單一繼承
在“單一繼承”這種最普通的形式中,派生類僅有一個(gè)基類,考慮如圖9.1所示的關(guān)系。


注意圖9.1中的從一般到特殊的過程。在類的層次設(shè)計(jì)中,可以發(fā)現(xiàn)一些普遍的特性,即派生類總是同基類有“kind of”關(guān)系。在圖9.1中書是一種印刷好的文檔而一本平裝書是一種書。

在圖9.1中的另一個(gè)值得注意點(diǎn)是Book既是派生類(從PrintedDocument中派生),也是基類(PaperbackBook是從Book派生的)。下面的例子是這種類層次的一個(gè)輪廓性的說明。

class PrintedDocument

{

//成員表

};

//Book是從PrintedDocument中派生的

class Book:public PrintedDocument

{

//成員表

};

//PaperbackBook是從Book中派生

class PaperbackBook: public Book

{

//成員表

};

PrintedDocument作為Book的直接基類,它同時(shí)也是PaperbackBook的非直接基類。直接基類和非直接基類的區(qū)別在于直接基類出現(xiàn)在類說明的基類表中,而非直接基類不出現(xiàn)在基類表中。

每個(gè)派生類的說明是在基類的說明之后說明的, 因此對(duì)于基類僅只給出一個(gè)前向引用的說明是不夠的,必須是完全的說明。

在前面的例子中,使用的訪問說明符是public。公有繼承、私有繼承以及保護(hù)的繼承在第10章“成員訪問控制”中講述。

一個(gè)類可以作為很多特別類的基類,如圖9.2所示。


在圖9.2中的圖叫“有向無環(huán)圖”(DAG)。有一些類是多個(gè)派生類的基類。但反過來不是真的:對(duì)于任意給的派生類僅有一個(gè)直接基類。圖9.2描繪了單一繼承的結(jié)構(gòu)。

注意:有向無環(huán)圖并不僅用于單一繼承。它們也可以用于多重繼承圖。這一主題將在下一節(jié)中的“多重繼承”中論述。

在繼承中,派生類含有基類的成員加上任何你新增的成員。結(jié)果派生類可以引用基類的成員(除非這些成員在派生類中重定義了)。當(dāng)在派生類中重定義直接基類或間接基類的成員時(shí),可以使用范圍分辨符(::)引用這些成員?紤]下面的代碼:

class Document
{
public:
char * Name;//文檔名稱
void PrintNameOf(); //打印名稱
};
//實(shí)現(xiàn)類Document的PrintNameOf函數(shù)

void Document::PrintNameOf()
{
cout << Name << end ;
}
class Book:public Document
{
public:
Book(char *name, long pagecount);
private:
long PageCount;
};
//class Book 構(gòu)造函數(shù)

Book::Book (char *name, long pagecount)
{
Name=mew char [strlen(name)+1];
strcpy (Name,name);
PageCount=pagecount;
};
注意,Book的構(gòu)造函數(shù)(Book::Book)具有對(duì)數(shù)據(jù)成員Name的訪問權(quán)。在程序中可以按如下方式創(chuàng)建Book類對(duì)象并使用之。

//創(chuàng)建一個(gè)Book類的新對(duì)象,這將激活構(gòu)造函數(shù)Book:BookBook

LibraryBook ("Programming Windows,2nd Ed",994);

...

//使用從Document中繼承的函數(shù)PrintNameOf.

LibraryBook.PrintNameOf();如前面例子所示,類成員和繼承的數(shù)據(jù)與函數(shù)以一致的方式引用。如果類Book所調(diào)用的PrintNameOf是由類Book重新定義實(shí)現(xiàn)的,則原來屬于類Document的PrintNameOf函數(shù)只能用范圍分辯符(::)才能使用:

class Book:public Document
{
Book(char *name,long pagecount);
void PrintNameOf();
long PageCount;
};
void Book::PrintNameOf()
{
cout<<"Name of Book:";
Document::PrintNameOf();
}
只要有一個(gè)可訪問的、無二義性的基類,派生類的指針和引用可以隱含地轉(zhuǎn)換為它們基類的指針和引用。下面的例子證實(shí)了這種使用指針的概念(同樣也適用于引用):

#include <iostream.h>
void main()
{
Document * DocLib[10]; //10個(gè)文檔的庫
for (int i=0; i<10; ++i)
{
cout<<"Type of document:"
<<"P)aperback,M)agazine,H)elp File,C)BT"
<< endl;
char CDocType;
cin >>CDocType;
switch(tolower(CDocType))
{
case 'p':
DocLib[i]=new PaperbackBook;
break;
case 'm':
DocLib[i]=new Magazine;
break;
case 'h':
DocLib[i]=new HelpFile;
break;
case 'c':
DocLib[i]=new ComputerBasedTraining;
break;
default:
--i;
break;
}
}
for (i=0; i<10; ++i)
DocLib[i]->PrintNameOf();
}
在前面例子的SWITCH語句中,創(chuàng)建了不同類型的對(duì)象。這一點(diǎn)依賴于用戶對(duì)CDocType對(duì)象所作出的說明。然而這些類型都是從類Document中派生出來的,故可以隱含地轉(zhuǎn)換為Document*。結(jié)果是DocLib成為一個(gè)“相似鏈表”(heterogeneous list)。此鏈表所包含的是不同種類的對(duì)象,其中的所有對(duì)象并不是有相同的類型。

因?yàn)镈ocument類有一個(gè)PrintNameOf函數(shù)。因此它能夠打印圖書館中每本書的名稱,但對(duì)于Document類型來說有一些信息會(huì)省略掉了(如:Book的總頁數(shù),HelpFile的字節(jié)數(shù)等)。

注意:強(qiáng)制基類去實(shí)現(xiàn)一個(gè)如PrintNameOf的函數(shù),通常不是一個(gè)很好的設(shè)計(jì),本章后面的“虛擬函數(shù)”中提供了一個(gè)可替換的設(shè)計(jì)方法。

多重繼承

C++的后期的一些版本為繼承引入了“多重繼承”模式。在一個(gè)多重繼承的圖中,派生類可以有多個(gè)直接基類?紤]圖9.3。


9.3所示的圖中,顯示了一個(gè)CollectibleString類。該類既像Collectible類(一種可包容聚集的類),又像String類。對(duì)于派生類需要多個(gè)基類的屬性的問題,多重繼承是一種很好的解決辦法。因而也很容易派生出CollectibleCustomer和CollectibleWindow等等。

對(duì)于一個(gè)特定的程序如果每個(gè)類的屬性并不是全部要求使用,則每個(gè)類可以單獨(dú)使用或者同別的類聯(lián)合在一起使用。因此把圖9.3所描繪的類層次作為基礎(chǔ),用戶很容易組織出不可收集的字符串或可收集的非字符串。對(duì)于使用單一繼承,則沒有這種便利性。

虛基類層次 有一些類層次很龐大,但有很多東西很普遍。這些普遍的代碼在基類中實(shí)現(xiàn)了,然而在派生類中又實(shí)現(xiàn)了特殊的代碼。

對(duì)于基類來說重要的是建立一種機(jī)制,通過這種機(jī)制派生類能夠完成大量的函數(shù)機(jī)能。

這種機(jī)制通常是用虛函數(shù)來實(shí)現(xiàn)的。有時(shí),基類為這些函數(shù)提供了一個(gè)缺省的實(shí)現(xiàn)。如在圖9.2的Document類層次中,兩個(gè)重要的函數(shù)是Identify和WhereIs。當(dāng)調(diào)用Identify函數(shù)時(shí),返回一個(gè)正確的標(biāo)識(shí)。對(duì)于各種文檔來說正確的是:對(duì)于Book,調(diào)用如doc->Identify()的函數(shù)必須返回ISBN編號(hào);而對(duì)于一個(gè)HelpFile返回產(chǎn)品名和版本號(hào)更合理一些。同樣,WhereIs函數(shù)對(duì)于一本書來說應(yīng)該返回行和書架號(hào),但對(duì)于HelpFile就應(yīng)該返回它的磁盤位置,也許是一個(gè)目錄和名稱。

了解到所有的Identify和WhereIs的函數(shù)實(shí)現(xiàn)返回的是同種類型的信息,這一點(diǎn)很重要。在這個(gè)例子中,恰好是一種描述性字符串。

這些函數(shù)可以作為虛擬函數(shù)來實(shí)現(xiàn),然后用指向基類的指針來調(diào)用,對(duì)于實(shí)際代碼的聯(lián)結(jié)將在運(yùn)行時(shí)決定,以選擇正確的Identify和WhereIs函數(shù)。

類協(xié)議的實(shí)現(xiàn)

類可以實(shí)現(xiàn)為要強(qiáng)制使用某些協(xié)議。這些類稱為“抽象類”,因?yàn)椴荒転檫@種類類型創(chuàng)建對(duì)象。它們僅僅是為了派生別的類而存在。

當(dāng)一個(gè)類中含有純虛擬函數(shù)或當(dāng)他們繼承了某些純虛擬函數(shù)卻又沒有為它們提供一個(gè)實(shí)現(xiàn)時(shí),該類稱為抽象類。純虛擬函數(shù)是用純說明符定義的虛擬函數(shù)。如下:

virtual char *Identify()=0;

基類Document把如下一些協(xié)議強(qiáng)加給派生類。

* 為Identify函數(shù)提供一個(gè)合適的實(shí)現(xiàn)

* 為WhereIs函數(shù)提供一個(gè)合適的實(shí)現(xiàn)

在設(shè)計(jì)Document類時(shí),通過說明這種協(xié)議,類設(shè)計(jì)者可以確保如不提供Identify和WhereIs函數(shù)則不能實(shí)現(xiàn)非抽象類。因而Document類含有如下說明:

class Document
{
public:
... 
//對(duì)派生類的要求,它們必須實(shí)現(xiàn)下面這些函數(shù)
virtual char *Identify()=0;
virtual char *WhereIs()=0;
...
};
基 類

如前面討論的,繼承過程創(chuàng)建的新的派生類是由基類的成員加上由派生類新加的成員組成。在多重繼承中,可以構(gòu)造層次圖,其中同一基類可以是多個(gè)派生類的一部分。圖9.4顯示了這種圖。


在圖9.4中以圖的形象表達(dá)了CollectibleString和CollectibleSortable的組成。然而,基類Collectible通過路徑CollectibleSortable以及CollectibleString到達(dá)類CollectibleSortableString。為了消除這種冗余,當(dāng)這些類被繼承時(shí),可以說明為虛擬基類。

有關(guān)說明虛擬基類以及帶有虛擬基類的對(duì)象是如何組成的,見本章后面的“虛擬基類”。



標(biāo)簽:繼承派生多態(tài)