(public) inheritance 這個(gè)表面上簡(jiǎn)單易懂的觀念,一旦被近距離審視,就會(huì)被證明是由兩個(gè)相互獨(dú)立的部分組成的:inheritance of function interfaces(函數(shù)接口的繼承)和 inheritance of function implementations(函數(shù)實(shí)現(xiàn)的繼承)。這兩種 inheritance 之間的差異正好符合本書(shū) Introduction 中論述的 function declarations(函數(shù)聲明)和 function definitions(函數(shù)定義)之間的差異。
作為一個(gè) class 的設(shè)計(jì)者,有的時(shí)候你想要 derived classes 只繼承一個(gè) member function 的 interface (declaration)。有的時(shí)候你想要 derived classes 既繼承 interface(接口)也繼承 implementation(實(shí)現(xiàn)),但你要允許它們替換他們繼承到的 implementation。還有的時(shí)候你想要 derived classes 繼承一個(gè)函數(shù)的 interface(接口)和 implementation(實(shí)現(xiàn)),而不允許它們替換任何東西。
為了更好地感覺(jué)這些選擇之間的不同之處,考慮一個(gè)在圖形應(yīng)用程序中表示幾何圖形的 class hierarchy(類(lèi)繼承體系):
class Shape {
public:
virtual void draw() const = 0;
virtual void error(const std::string& msg);
int objectID() const;
...
};
class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };
Shape 是一個(gè) abstract class(抽象類(lèi)),它的 pure virtual function(純虛擬函數(shù))表明了這一點(diǎn)。作為結(jié)果,客戶不能創(chuàng)建 Shape class 的實(shí)例,只能創(chuàng)建從它繼承的 classes 的實(shí)例。但是,Shape 對(duì)所有從它(公有)繼承的類(lèi)施加了非常強(qiáng)大的影響,因?yàn)?BR> 成員函數(shù) interfaces are always inherited。就像 Item 32 解釋的,public inheritance 意味著 is-a,所以對(duì)一個(gè) base class 來(lái)說(shuō)成立的任何東西,對(duì)于它的 derived classes 也必須成立。因此,如果一個(gè)函數(shù)適用于一個(gè) class,它也一定適用于它的 derived classes。
Shape class 中聲明了三個(gè)函數(shù)。第一個(gè),draw,在一個(gè)明確的顯示設(shè)備上畫(huà)出當(dāng)前對(duì)象。第二個(gè),error,如果 member functions 需要報(bào)告一個(gè)錯(cuò)誤,就調(diào)用它。第三個(gè),objectID,返回當(dāng)前對(duì)象的整型標(biāo)識(shí)符。每一個(gè)函數(shù)都用不同的方式聲明:draw 是一個(gè) pure virtual function(純虛擬函數(shù));error 是一個(gè) simple (impure?) virtual function(簡(jiǎn)單虛擬函數(shù));而 objectID 是一個(gè) non-virtual function(非虛擬函數(shù))。這些不同的聲明暗示了什么呢?
考慮第一個(gè) pure virtual function(純虛擬函數(shù))draw:
class Shape {
public:
virtual void draw() const = 0;
...
};
pure virtual functions(純虛擬函數(shù))的兩個(gè)最顯著的特性是它們必須被任何繼承它們的具體類(lèi)重新聲明,和抽象類(lèi)中一般沒(méi)有它們的定義。把這兩個(gè)特性加在一起,你應(yīng)該認(rèn)識(shí)到。
聲明一個(gè) pure virtual function(純虛擬函數(shù))的目的是使 derived classes 繼承一個(gè)函數(shù) interface only。
這就使 Shape::draw function 具有了完整的意義,因?yàn)樗笏械?Shape 對(duì)象必須能夠畫(huà)出來(lái)是合情合理的,但是 Shape class 本身不能為這個(gè)函數(shù)提供一個(gè)合乎情理的缺省的實(shí)現(xiàn)。例如,畫(huà)一個(gè)橢圓的算法和畫(huà)一個(gè)矩形的算法是非常不同的,Shape::draw 的聲明告訴具體 derived classes 的設(shè)計(jì)者:“你必須提供一個(gè) draw function,但是我對(duì)于你如何實(shí)現(xiàn)它不發(fā)表意見(jiàn)?!?BR> 順便提一句,為一個(gè) pure virtual function(純虛擬函數(shù))提供一個(gè)定義是有可能的。也就是說(shuō),你可以為 Shape::draw 提供一個(gè)實(shí)現(xiàn),而 C 也不會(huì)抱怨什么,但是調(diào)用它的方法是用 class name 限定修飾這個(gè)調(diào)用:
Shape *ps = new Shape; // error! Shape is abstract
Shape *ps1 = new Rectangle; // fine
ps1->draw(); // calls Rectangle::draw
Shape *ps2 = new Ellipse; // fine
ps2->draw(); // calls Ellipse::draw
ps1->Shape::draw(); // calls Shape::draw
ps2->Shape::draw(); // calls Shape::draw
除了幫助你在雞尾酒會(huì)上給同行程序員留下印象外,這個(gè)特性通常沒(méi)什么用處,然而,就像下面你將看到的,它能用來(lái)作為一個(gè)“為 simple (impure) virtual functions 提供一個(gè) safer-than-usual 的實(shí)現(xiàn)”的機(jī)制。
simple virtual functions 背后的故事和 pure virtuals 有一點(diǎn)不同。derived classes 照常還是繼承函數(shù)的 interface,但是 simple virtual functions 提供了一個(gè)可以被 derived classes 替換的實(shí)現(xiàn)。如果你為此考慮一陣兒,你就會(huì)認(rèn)識(shí)到
聲明一個(gè) simple virtual function 的目的是讓 derived classes 繼承一個(gè)函數(shù) interface as well as a default implementation。
考慮 Shape::error 的情況:
class Shape {
public:
virtual void error(const std::string& msg);
...
};
interface 要求每一個(gè) class 必須支持一個(gè)在遭遇到錯(cuò)誤時(shí)被調(diào)用的函數(shù),但是每一個(gè) class 可以自由地用它覺(jué)得合適的任何方法處理錯(cuò)誤。如果一個(gè) class 不需要做什么特別的事情,它可以僅僅求助于 Shape class 中提供的錯(cuò)誤處理的缺省版本。也就是說(shuō),Shape::error 的聲明告訴 derived classes 的設(shè)計(jì)者:“你應(yīng)該支持一個(gè) error function,但如果你不想自己寫(xiě),你可以求助 Shape class 中的缺省版本?!?BR> 結(jié)果是:允許 simple virtual functions 既指定一個(gè)函數(shù)接口又指定一個(gè)缺省實(shí)現(xiàn)是危險(xiǎn)的。來(lái)看一下為什么,考慮一個(gè) XYZ 航空公司的飛機(jī)的 hierarchy(繼承體系)。XYZ 只有兩種飛機(jī),Model A 和 Model B,它們都嚴(yán)格地按照同樣的方法飛行。于是,XYZ 設(shè)計(jì)如下 hierarchy(繼承體系):
class Airport { ... }; // represents airports
class Airplane {
public:
virtual void fly(const Airport& destination);
...
};
void Airplane::fly(const Airport&a
mp; destination)
{
default code for flying an airplane to the given destination
}
class ModelA: public Airplane { ... };
class ModelB: public Airplane { ... };
為了表述所有的飛機(jī)必須支持一個(gè) fly 函數(shù),并為了“不同機(jī)型可能(在理論上)需要不同的對(duì) fly 的實(shí)現(xiàn)”的事實(shí),Airplane::fly 被聲明為 virtual。然而,為了避免在 ModelA 和 ModelB classes 中些重復(fù)的代碼,缺省的飛行行為由 Airplane::fly 的函數(shù)體提供,供 ModelA 和 ModelB 繼承。
這是一個(gè)經(jīng)典的 object-oriented 設(shè)計(jì)。因?yàn)閮蓚€(gè) classes 共享一個(gè)通用特性(它們實(shí)現(xiàn) fly 的方法),所以這個(gè)通用特性就被轉(zhuǎn)移到一個(gè) base class 之中,并由兩個(gè) classes 來(lái)繼承這個(gè)特性。這個(gè)設(shè)計(jì)使得通用特性變得清楚明白,避免了代碼重復(fù),提升了未來(lái)的可擴(kuò)展性,簡(jiǎn)化了長(zhǎng)期的維護(hù)——因?yàn)?object-oriented 技術(shù),所有這些東西都受到很高的追捧。XYZ 航空公司應(yīng)該引以為榮。
現(xiàn)在,假設(shè) XYZ 公司的財(cái)富增長(zhǎng)了,決定引進(jìn)一種新機(jī)型,Model C。Model C 在某些方面與 Model A 和 Model B 不同。特別是,它的飛行不同。
XYZ 公司的程序員在 hierarchy(繼承體系)中增加了 Model C 的 class,但是由于他們匆匆忙忙地讓新的機(jī)型投入服務(wù),他們忘記了重定義 fly function:
class ModelC: public Airplane {
... // no fly function is declared
};
作為一個(gè) class 的設(shè)計(jì)者,有的時(shí)候你想要 derived classes 只繼承一個(gè) member function 的 interface (declaration)。有的時(shí)候你想要 derived classes 既繼承 interface(接口)也繼承 implementation(實(shí)現(xiàn)),但你要允許它們替換他們繼承到的 implementation。還有的時(shí)候你想要 derived classes 繼承一個(gè)函數(shù)的 interface(接口)和 implementation(實(shí)現(xiàn)),而不允許它們替換任何東西。
為了更好地感覺(jué)這些選擇之間的不同之處,考慮一個(gè)在圖形應(yīng)用程序中表示幾何圖形的 class hierarchy(類(lèi)繼承體系):
class Shape {
public:
virtual void draw() const = 0;
virtual void error(const std::string& msg);
int objectID() const;
...
};
class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };
Shape 是一個(gè) abstract class(抽象類(lèi)),它的 pure virtual function(純虛擬函數(shù))表明了這一點(diǎn)。作為結(jié)果,客戶不能創(chuàng)建 Shape class 的實(shí)例,只能創(chuàng)建從它繼承的 classes 的實(shí)例。但是,Shape 對(duì)所有從它(公有)繼承的類(lèi)施加了非常強(qiáng)大的影響,因?yàn)?BR> 成員函數(shù) interfaces are always inherited。就像 Item 32 解釋的,public inheritance 意味著 is-a,所以對(duì)一個(gè) base class 來(lái)說(shuō)成立的任何東西,對(duì)于它的 derived classes 也必須成立。因此,如果一個(gè)函數(shù)適用于一個(gè) class,它也一定適用于它的 derived classes。
Shape class 中聲明了三個(gè)函數(shù)。第一個(gè),draw,在一個(gè)明確的顯示設(shè)備上畫(huà)出當(dāng)前對(duì)象。第二個(gè),error,如果 member functions 需要報(bào)告一個(gè)錯(cuò)誤,就調(diào)用它。第三個(gè),objectID,返回當(dāng)前對(duì)象的整型標(biāo)識(shí)符。每一個(gè)函數(shù)都用不同的方式聲明:draw 是一個(gè) pure virtual function(純虛擬函數(shù));error 是一個(gè) simple (impure?) virtual function(簡(jiǎn)單虛擬函數(shù));而 objectID 是一個(gè) non-virtual function(非虛擬函數(shù))。這些不同的聲明暗示了什么呢?
考慮第一個(gè) pure virtual function(純虛擬函數(shù))draw:
class Shape {
public:
virtual void draw() const = 0;
...
};
pure virtual functions(純虛擬函數(shù))的兩個(gè)最顯著的特性是它們必須被任何繼承它們的具體類(lèi)重新聲明,和抽象類(lèi)中一般沒(méi)有它們的定義。把這兩個(gè)特性加在一起,你應(yīng)該認(rèn)識(shí)到。
聲明一個(gè) pure virtual function(純虛擬函數(shù))的目的是使 derived classes 繼承一個(gè)函數(shù) interface only。
這就使 Shape::draw function 具有了完整的意義,因?yàn)樗笏械?Shape 對(duì)象必須能夠畫(huà)出來(lái)是合情合理的,但是 Shape class 本身不能為這個(gè)函數(shù)提供一個(gè)合乎情理的缺省的實(shí)現(xiàn)。例如,畫(huà)一個(gè)橢圓的算法和畫(huà)一個(gè)矩形的算法是非常不同的,Shape::draw 的聲明告訴具體 derived classes 的設(shè)計(jì)者:“你必須提供一個(gè) draw function,但是我對(duì)于你如何實(shí)現(xiàn)它不發(fā)表意見(jiàn)?!?BR> 順便提一句,為一個(gè) pure virtual function(純虛擬函數(shù))提供一個(gè)定義是有可能的。也就是說(shuō),你可以為 Shape::draw 提供一個(gè)實(shí)現(xiàn),而 C 也不會(huì)抱怨什么,但是調(diào)用它的方法是用 class name 限定修飾這個(gè)調(diào)用:
Shape *ps = new Shape; // error! Shape is abstract
Shape *ps1 = new Rectangle; // fine
ps1->draw(); // calls Rectangle::draw
Shape *ps2 = new Ellipse; // fine
ps2->draw(); // calls Ellipse::draw
ps1->Shape::draw(); // calls Shape::draw
ps2->Shape::draw(); // calls Shape::draw
除了幫助你在雞尾酒會(huì)上給同行程序員留下印象外,這個(gè)特性通常沒(méi)什么用處,然而,就像下面你將看到的,它能用來(lái)作為一個(gè)“為 simple (impure) virtual functions 提供一個(gè) safer-than-usual 的實(shí)現(xiàn)”的機(jī)制。
simple virtual functions 背后的故事和 pure virtuals 有一點(diǎn)不同。derived classes 照常還是繼承函數(shù)的 interface,但是 simple virtual functions 提供了一個(gè)可以被 derived classes 替換的實(shí)現(xiàn)。如果你為此考慮一陣兒,你就會(huì)認(rèn)識(shí)到
聲明一個(gè) simple virtual function 的目的是讓 derived classes 繼承一個(gè)函數(shù) interface as well as a default implementation。
考慮 Shape::error 的情況:
class Shape {
public:
virtual void error(const std::string& msg);
...
};
interface 要求每一個(gè) class 必須支持一個(gè)在遭遇到錯(cuò)誤時(shí)被調(diào)用的函數(shù),但是每一個(gè) class 可以自由地用它覺(jué)得合適的任何方法處理錯(cuò)誤。如果一個(gè) class 不需要做什么特別的事情,它可以僅僅求助于 Shape class 中提供的錯(cuò)誤處理的缺省版本。也就是說(shuō),Shape::error 的聲明告訴 derived classes 的設(shè)計(jì)者:“你應(yīng)該支持一個(gè) error function,但如果你不想自己寫(xiě),你可以求助 Shape class 中的缺省版本?!?BR> 結(jié)果是:允許 simple virtual functions 既指定一個(gè)函數(shù)接口又指定一個(gè)缺省實(shí)現(xiàn)是危險(xiǎn)的。來(lái)看一下為什么,考慮一個(gè) XYZ 航空公司的飛機(jī)的 hierarchy(繼承體系)。XYZ 只有兩種飛機(jī),Model A 和 Model B,它們都嚴(yán)格地按照同樣的方法飛行。于是,XYZ 設(shè)計(jì)如下 hierarchy(繼承體系):
class Airport { ... }; // represents airports
class Airplane {
public:
virtual void fly(const Airport& destination);
...
};
void Airplane::fly(const Airport&a
mp; destination)
{
default code for flying an airplane to the given destination
}
class ModelA: public Airplane { ... };
class ModelB: public Airplane { ... };
為了表述所有的飛機(jī)必須支持一個(gè) fly 函數(shù),并為了“不同機(jī)型可能(在理論上)需要不同的對(duì) fly 的實(shí)現(xiàn)”的事實(shí),Airplane::fly 被聲明為 virtual。然而,為了避免在 ModelA 和 ModelB classes 中些重復(fù)的代碼,缺省的飛行行為由 Airplane::fly 的函數(shù)體提供,供 ModelA 和 ModelB 繼承。
這是一個(gè)經(jīng)典的 object-oriented 設(shè)計(jì)。因?yàn)閮蓚€(gè) classes 共享一個(gè)通用特性(它們實(shí)現(xiàn) fly 的方法),所以這個(gè)通用特性就被轉(zhuǎn)移到一個(gè) base class 之中,并由兩個(gè) classes 來(lái)繼承這個(gè)特性。這個(gè)設(shè)計(jì)使得通用特性變得清楚明白,避免了代碼重復(fù),提升了未來(lái)的可擴(kuò)展性,簡(jiǎn)化了長(zhǎng)期的維護(hù)——因?yàn)?object-oriented 技術(shù),所有這些東西都受到很高的追捧。XYZ 航空公司應(yīng)該引以為榮。
現(xiàn)在,假設(shè) XYZ 公司的財(cái)富增長(zhǎng)了,決定引進(jìn)一種新機(jī)型,Model C。Model C 在某些方面與 Model A 和 Model B 不同。特別是,它的飛行不同。
XYZ 公司的程序員在 hierarchy(繼承體系)中增加了 Model C 的 class,但是由于他們匆匆忙忙地讓新的機(jī)型投入服務(wù),他們忘記了重定義 fly function:
class ModelC: public Airplane {
... // no fly function is declared
};