国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設(shè)計 > 正文

C++箴言:接口繼承和實現(xiàn)繼承

2019-11-17 05:18:26
字體:
供稿:網(wǎng)友
  (public) inheritance 這個表面上簡單易懂的觀念,一旦被近距離審閱,就會被證實是由兩個相互獨立的部分組成的:inheritance of function interfaces(函數(shù)接口的繼續(xù))和 inheritance of function implementations(函數(shù)實現(xiàn)的繼續(xù))。這兩種 inheritance 之間的差異正好符合本書 IntrodUCtion 中論述的 function declarations(函數(shù)聲明)和 function definitions(函數(shù)定義)之間的差異。  作為一個 class 的設(shè)計者,有的時候你想要 derived classes 只繼續(xù)一個 member function 的 interface (declaration)。有的時候你想要 derived classes 既繼續(xù) interface(接口)也繼續(xù) implementation(實現(xiàn)),但你要答應(yīng)它們替換他們繼續(xù)到的 implementation。還有的時候你想要 derived classes 繼續(xù)一個函數(shù)的 interface(接口)和 implementation(實現(xiàn)),而不答應(yīng)它們替換任何東西。

  為了更好地感覺這些選擇之間的不同之處,考慮一個在圖形應(yīng)用程序中表示幾何圖形的 class hierarchy(類繼續(xù)體系):

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 是一個 abstract class(抽象類),它的 pure virtual function(純虛擬函數(shù))表明了這一點。作為結(jié)果,客戶不能創(chuàng)建 Shape class 的實例,只能創(chuàng)建從它繼續(xù)的 classes 的實例。但是,Shape 對所有從它(公有)繼續(xù)的類施加了非常強大的影響,因為

  成員函數(shù) interfaces are always inherited。就像 Item 32 解釋的,public inheritance 意味著 is-a,所以對一個 base class 來說成立的任何東西,對于它的 derived classes 也必須成立。因此,假如一個函數(shù)適用于一個 class,它也一定適用于它的 derived classes。

  Shape class 中聲明了三個函數(shù)。第一個,draw,在一個明確的顯示設(shè)備上畫出當前對象。第二個,error,假如 member functions 需要報告一個錯誤,就調(diào)用它。第三個,objectID,返回當前對象的唯一整型標識符。每一個函數(shù)都用不同的方式聲明:draw 是一個 pure virtual function(純虛擬函數(shù));error 是一個 simple (impure?) virtual function(簡單虛擬函數(shù));而 objectID 是一個 non-virtual function(非虛擬函數(shù))。這些不同的聲明暗示了什么呢?

  考慮第一個 pure virtual function(純虛擬函數(shù))draw:

class Shape {
public:
virtual void draw() const = 0;
...
};
  pure virtual functions(純虛擬函數(shù))的兩個最顯著的特性是它們必須被任何繼續(xù)它們的具體類重新聲明,和抽象類中一般沒有它們的定義。把這兩個特性加在一起,你應(yīng)該熟悉到。

  聲明一個 pure virtual function(純虛擬函數(shù))的目的是使 derived classes 繼續(xù)一個函數(shù) interface only。

  這就使 Shape::draw function 具有了完整的意義,因為它要求所有的 Shape 對象必須能夠畫出來是合情合理的,但是 Shape class 本身不能為這個函數(shù)提供一個合乎情理的缺省的實現(xiàn)。例如,畫一個橢圓的算法和畫一個矩形的算法是非常不同的,Shape::draw 的聲明告訴具體 derived classes 的設(shè)計者:“你必須提供一個 draw function,但是我對于你如何實現(xiàn)它不發(fā)表意見。”

  順便提一句,為一個 pure virtual function(純虛擬函數(shù))提供一個定義是有可能的。也就是說,你可以為 Shape::draw 提供一個實現(xiàn),而 C++ 也不會抱怨什么,但是調(diào)用它的唯一方法是用 class name 限定修飾這個調(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
  除了幫助你在雞尾酒會上給同行程序員留下印象外,這個特性通常沒什么用處,然而,就像下面你將看到的,它能用來作為一個“為 simple (impure) virtual functions 提供一個 safer-than-usual 的實現(xiàn)”的機制。

  simple virtual functions 背后的故事和 pure virtuals 有一點不同。derived classes 照常還是繼續(xù)函數(shù)的 interface,但是 simple virtual functions 提供了一個可以被 derived classes 替換的實現(xiàn)。假如你為此考慮一陣兒,你就會熟悉到

  聲明一個 simple virtual function 的目的是讓 derived classes 繼續(xù)一個函數(shù) interface as well as a default implementation。

  考慮 Shape::error 的情況:

class Shape {
public:
virtual void error(const std::string& msg);
...
};
  interface 要求每一個 class 必須支持一個在遭碰到錯誤時被調(diào)用的函數(shù),但是每一個 class 可以自由地用它覺得合適的任何方法處理錯誤。假如一個 class 不需要做什么非凡的事情,它可以僅僅求助于 Shape class 中提供的錯誤處理的缺省版本。也就是說,Shape::error 的聲明告訴 derived classes 的設(shè)計者:“你應(yīng)該支持一個 error function,但假如你不想自己寫,你可以求助 Shape class 中的缺省版本。”

  結(jié)果是:答應(yīng) simple virtual functions 既指定一個函數(shù)接口又指定一個缺省實現(xiàn)是危險的。來看一下為什么,考慮一個 XYZ 航空公司的飛機的 hierarchy(繼續(xù)體系)。XYZ 只有兩種飛機,Model A 和 Model B,它們都嚴格地按照同樣的方法飛行。于是,XYZ 設(shè)計如下 hierarchy(繼續(xù)體系):

class Airport { ... }; // rePResents airports

class Airplane {
public:
virtual void fly(const Airport& destination);

...

};

void Airplane::fly(const Airport& destination)
{
default code for flying an airplane to the given destination
}

class ModelA: public Airplane { ... };

class ModelB: public Airplane { ... };

  為了表述所有的飛機必須支持一個 fly 函數(shù),并為了“不同機型可能(在理論上)需要不同的對 fly 的實現(xiàn)”的事實,Airplane::fly 被聲明為 virtual。然而,為了避免在 ModelA 和 ModelB classes 中些重復的代碼,缺省的飛行行為由 Airplane::fly 的函數(shù)體提供,供 ModelA 和 ModelB 繼續(xù)。

  這是一個經(jīng)典的 object-oriented 設(shè)計。因為兩個 classes 共享一個通用特性(它們實現(xiàn) fly 的方法),所以這個通用特性就被轉(zhuǎn)移到一個 base class 之中,并由兩個 classes 來繼續(xù)這個特性。這個設(shè)計使得通用特性變得清楚明白,避免了代碼重復,提升了未來的可擴展性,簡化了長期的維護——因為 object-oriented 技術(shù),所有這些東西都受到很高的追捧。XYZ 航空公司應(yīng)該引以為榮。  現(xiàn)在,假設(shè) XYZ 公司的財富增長了,決定引進一種新機型,Model C。Model C 在某些方面與 Model A 和 Model B 不同。非凡是,它的飛行不同。

  XYZ 公司的程序員在 hierarchy(繼續(xù)體系)中增加了 Model C 的 class,但是由于他們匆匆忙忙地讓新的機型投入服務(wù),他們忘記了重定義 fly function:

class ModelC: public Airplane {

... // no fly function is declared
};
  于是,在他們的代碼中,就出現(xiàn)了類似這樣的東西:

Airport PDX(...); // PDX is the airport near my home

Airplane *pa = new ModelC;

...

pa->fly(PDX); // calls Airplane::fly!
  這是一個災(zāi)難:企圖讓一個 ModelC object 像一個 ModelA 或 ModelB 一樣飛行。這在旅行人群中可不是一種鼓舞人心的行為。

  這里的問題并不在于 Airplane::fly 有缺省的行為,而是在于 ModelC 被答應(yīng)不必明確說出它要做什么就可以繼續(xù)這一行為。幸運的是,“為 derived classes(派生類)提供缺省的行為,但是除非它們提出明確的要求,否則就不交給他們”是很輕易做到的。這個訣竅就是切斷 virtual function(虛擬函數(shù))的 interface(接口)和它的 default implementation(缺省實現(xiàn))之間的聯(lián)系。以下用的就是這個方法:

class Airplane {
public:
virtual void fly(const Airport& destination) = 0;

...

protected:
void defaultFly(const Airport& destination);
};

void Airplane::defaultFly(const Airport& destination)
{
default code for flying an airplane to the given destination
}
  注重 Airplane::fly 是被如何變成一個 pure virtual function(純虛擬函數(shù))的。它為飛行提供了 interface(接口)。那個缺省的實現(xiàn)也會出現(xiàn)在 Airplane class 中,但是現(xiàn)在它是一個獨立的函數(shù),defaultFly。像 ModelA 和 ModelB 這樣需要使用缺省行為的 Classes 只是需要在他們的 fly 的函數(shù)體中做一下對 defaultFly 的 inline 調(diào)用(但是請參見 Item 30 提供的關(guān)于 inline 化和 virtual functions(虛擬函數(shù))的交互作用的信息):

class ModelA: public Airplane {
public:
virtual void fly(const Airport& destination)
{ defaultFly(destination); }

...
};

class ModelB: public Airplane {
public:
virtual void fly(const Airport& destination)
{ defaultFly(destination); }

...
};
  對于 ModelC class,不可能在無意中繼續(xù)到不正確的 fly 的實現(xiàn),因為 Airplane 中的 pure virtual(純虛擬)強制要求 ModelC 提供的它自己的 fly 版本。

class ModelC: public Airplane {
public:
virtual void fly(const Airport& destination);

...
};

void ModelC::fly(const Airport& destination)
{
code for flying a ModelC airplane to the given destination
}
  這一方案并非十分安全(程序員還是能通過 copy-and-paste 使他們自己陷入麻煩),但是它比最初的設(shè)計更加可靠。至于 Airplane::defaultFly,它是 protected(保護的)是因為它完全是 Airplane 和它的 derived classes(派生類)的實現(xiàn)細節(jié)。使用飛機的客戶應(yīng)該只在意它能飛,而不必管飛行是如何實現(xiàn)的。

  Airplane::defaultFly 是一個 non-virtual function(非虛擬函數(shù))這一點也很重要。這是因為 derived class(派生類)不應(yīng)該重定義這個函數(shù),這是一個在 Item 36 中專門介紹的原則。假如 defaultFly 是 virtual(虛擬的),你就會碰到一個循環(huán)的問題:假如某些 derived class(派生類)應(yīng)該重定義 defaultFly 卻忘記了的時候會如何呢?

  一些人反對為 interface(接口)和 default implementation(缺省實現(xiàn))分別提供函數(shù),就像上面的 fly 和 defaultFly 那樣。首先,他們注重到,這樣做會導致類似的相關(guān)函數(shù)名污染 class namespace(類名字空間)的問題。然而他們?nèi)匀煌?interface(接口)和 default implementation(缺省實現(xiàn))應(yīng)該被分開。他們是怎樣解決這個表面上的矛盾呢?通過利用以下事實:pure virtual functions(純虛擬函數(shù))必須在 concrete derived classes(具體派生類)中被 redeclared(重聲明),但是它們也可以有它們自己的實現(xiàn)。以下就是 Airplane hierarchy(繼續(xù)體系)如何利用這一能力定義一個 pure virtual function(純虛擬函數(shù)):


class Airplane {
public:
virtual void fly(const Airport& destination) = 0;

...
};

void Airplane::fly(const Airport& destination) // an implementation of
{ // a pure virtual function
default code for flying an airplane to
the given destination
}

class ModelA: public Airplane {
public:
virtual void fly(const Airport& destination)
{ Airplane::fly(destination); }

...

};

class ModelB: public Airplane {
public:
virtual void fly(const Airport& destination)
{ Airplane::fly(destination); }

...

};

class ModelC: public Airplane {
public:
virtual void fly(const Airport& destination);

...

};

void ModelC::fly(const Airport& destination)
{
code for flying a ModelC airplane to the given destination
}
  除了用 pure virtual function(純虛擬函數(shù))Airplane::fly 的函數(shù)體代替了獨立函數(shù) Airplane::defaultFly 之外,這是一個和前面的幾乎完全相同的設(shè)計。本質(zhì)上,fly 可以被拆成兩個基本組件。它的 declaration(聲明)指定了它的 interface(接口)(這是 derived classes(派生類)必須使用的),而它的 definition(定義)指定它的缺省行為(這是 derived classes(派生類)可以使用的,但只是在他們明確要求這一點時)。將 fly 和 defaultFly 合并,無論如何,你失去了給予這兩個函數(shù)不同的保護層次的能力:原來是 protected 的代碼(通過位于 defaultFly 中實現(xiàn))現(xiàn)在成為 public(因為它位于 fly 中)。

  最后,我們看看 Shape 的 non-virtual function(非虛擬函數(shù)),objectID:

class Shape {
public:
int objectID() const;
...
};
  當一個 member function(成員函數(shù))是 non-virtual(非虛擬的)時,不應(yīng)該指望它在 derived classes(派生類)中的行為會有所不同。實際上,一個 non-virtual member function(非虛擬成員函數(shù))指定了一個 invariant over specialization(超越非凡化的不變量),因為不論一個 derived class(派生類)變得多么非凡,它都把它看作是不答應(yīng)變化的行為。如下所指除的,

  聲明一個 non-virtual function(非虛擬函數(shù))的目的是 to have derived classes inherit a function interface as well as a mandatory implementation(使派生類既繼續(xù)一個函數(shù)的接口,又繼續(xù)一個強制的實現(xiàn))。

  你可以這樣考慮 Shape::objectID 的聲明,“每一個 Shape object 有一個產(chǎn)生 object identifier(對象標識碼),而且這個 object identifier(對象標識碼)總是用同樣的方法計算出來的,這個方法是由 Shape::objectID 的定義決定的,而且 derived class(派生類)不應(yīng)該試圖改變它的做法。”因為一個 non-virtual function(非虛擬函數(shù))被看作一個 invariant over specialization(超越非凡化的不變量),在 derived class(派生類)中他絕不應(yīng)該被重定義,細節(jié)的討論參見 Item 36。

  對 pure virtual,simple virtual,和 non-virtual functions 的聲明的不同答應(yīng)你精確指定你需要 derived classes(派生類)繼續(xù)什么東西。分別是 interface only(僅有接口),interface and a default implementation(接口和一個缺省的實現(xiàn)),和 interface and a mandatory implementation(接口和一個強制的實現(xiàn))。因為這些不同的聲明類型意味著根本不同的意義,當你聲明你的 member functions(成員函數(shù))時你必須在它們之間仔細地選擇。假如你這樣做了,你應(yīng)該可以避免由缺乏經(jīng)驗的類設(shè)計者造成的兩個最常見的錯誤。

  第一個錯誤是聲明所有的函數(shù)為 non-virtual(非虛擬)。這沒有給 derived classes(派生類)的非凡化留出空間;non-virtual destructors(非虛擬析構(gòu)函數(shù))尤其有問題(參見 Item 7)。當然,完全有理由設(shè)計一個不作為 base class(基類)使用的類。在這種情況下,一套獨享的 non-virtual member functions(非虛擬成員函數(shù))是完全合理的。然而,更通常的情況下,這樣的類既可能出于對 virtual(虛擬)和 non-virtual functions(非虛擬函數(shù))之間區(qū)別的無知,也可能是對 virtual functions(虛擬函數(shù))的性能成本毫無根據(jù)的擔心的結(jié)果。事實是,幾乎任何作為 base class(基類)使用的類都會有 virtual functions(虛擬函數(shù))(還是參見 Item 7)。

  假如你關(guān)心 virtual functions(虛擬函數(shù))的成本,請答應(yīng)我提起基于經(jīng)驗的 80-20 規(guī)則(參見 Item 30),在一個典型的程序中的情況是,80% 的運行時間花費在執(zhí)行其中的 20% 的代碼上。這個規(guī)則是很重要的,因為它意味著,平均下來,你的函數(shù)調(diào)用中的 80% 可以被虛擬化而不會對你的程序的整體性能產(chǎn)生哪怕是最稍微的可察覺的影響。在你走進對“你是否能負擔得起一個 virtual function(虛擬函數(shù))的成本”憂慮的陰影之前,應(yīng)該使用一些簡單的預防措施,以確保你關(guān)注的是你的程序中能產(chǎn)生決定性不同的那 20%。

  另一個常見的錯誤聲明所有的 member functions(成員函數(shù))為 virtual(虛擬)。有時候這樣做是正確的—— Item 31 的 Interface classes(接口類)可以作為證據(jù)。然而,它也可能是缺乏表明態(tài)度的決心的類設(shè)計者的標志。某些函數(shù)在 derived classes(派生類)中不應(yīng)該被重定義,而且只要在這種情況下,你都應(yīng)該通過將那些函數(shù)聲明為 non-virtual(非虛擬)而明確地表達這一點。它不是為那些人服務(wù)的,他們假設(shè)假如他們只需花一些時間重定義你的所有函數(shù),你的類就會被所有的人用來做所有的事情,假如你有一個 invariant over specialization(超越非凡化的不變量),請直說,不必害怕!

  Things to Remember

  ·Inheritance of interface(接口繼續(xù))與 inheritance of implementation(實現(xiàn)繼續(xù))不同。在 public inheritance(公開繼續(xù))下,derived classes(派生類)總是繼續(xù) base class interfaces(基類接口)。

  ·Pure virtual functions(純虛擬函數(shù))指定 inheritance of interface only(僅有接口被繼續(xù))。

  ·Simple (impure) virtual functions(簡單虛擬函數(shù))指定 inheritance of interface(接口繼續(xù))加上 inheritance of a default implementation(缺省實現(xiàn)繼續(xù))。

  ·Non-virtual functions(非虛擬函數(shù))指定 inheritance of interface(接口繼續(xù))加上 inheritance of a mandatory implementation(強制實現(xiàn)繼續(xù))。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 垫江县| 集安市| 苍山县| 霍林郭勒市| 宣武区| 丹巴县| 泾源县| 青海省| 诸城市| 秭归县| 大冶市| 安化县| 丽水市| 来安县| 抚远县| 句容市| 千阳县| 勐海县| 容城县| 麦盖提县| 义马市| 高碑店市| 留坝县| 南陵县| 宣威市| 盐源县| 玉屏| 汉阴县| 江北区| 崇仁县| 麻城市| 安阳县| 东港市| 城步| 昌都县| 九江市| 中阳县| 阿拉善盟| 大悟县| 高要市| 桓台县|