青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Shuffy

不斷的學習,不斷的思考,才能不斷的進步.Let's do better together!
posts - 102, comments - 43, trackbacks - 0, articles - 19
【轉(zhuǎn)】http://m.shnenglu.com/tiandejian/archive/2008/03/17/ec_32.html

第六章.        繼承和面向?qū)ο笤O計

面向?qū)ο蟮某绦蛟O計( OOP )風靡計算機軟件界已經(jīng)有 20 個年頭了,因此,你或多或少會與繼承、派生以及虛函數(shù)這些事物有所接觸。即使你僅僅使用 C 語言編程,那你也難以徹底逃離 OOP 的大氣候。

然而, C++ 中的 OOP 與你過去常見的可能有所不同。繼承可以是單一的也可以是多重的,同時每個繼承鏈接可以是公 有的 (public) ,可以是受保護的 (protected) ,也可以是私有的 (private) 。每個鏈接也可以分為虛擬 (virtual) 和非虛擬兩種。另外,成員函數(shù)也有選擇的范圍:虛擬的?非虛擬的?純虛擬的?此外,與其他語言特征進行交互也有需要考慮的問題:默認參數(shù)值是如何與虛函數(shù)相交互的?繼承是如何影響 C++ 的名字查找規(guī)則的?還有設計的問題:如果需要將一個類設計成可修改的,那么虛函數(shù)是實現(xiàn)這一特性的最優(yōu)途徑嗎?

本章就會針對這些問題為大家一一道來。而后,我還將向大家解釋 C++ 種特殊功能的真正含義。——也就是當你使用一種特定的構造方式時,你真正表達出的內(nèi)容。比如說,公共繼承意味著“ A 是一個 B ”,倘若你讓其表示其它的含義,那么你就會惹上麻煩。類似地,一個虛函數(shù)意味著“接口必須被繼承”,然而一個非虛函數(shù)則意味著“接口和實現(xiàn)必須都被繼承。”一個 C++ 程序員如果不能恰當?shù)膮^(qū)分這些內(nèi)容的含義,那么他的編程生涯就會顯得步履維艱。

如果你能夠深入了解 C++ 中浩瀚特征的方方面面,你將發(fā)現(xiàn)你對 OOP 的見解將有質(zhì)的飛躍。你的見解將決定你對軟件系統(tǒng)的整體認識。一旦你對 C++ 的認識變得全面而成熟了,此時你的 C++ 之路將變得一馬平川。


 

第32條:     確保公共繼承以“ A 是一個 B ”形式進行

威廉迪蒙在他的書《一些人睡去,而另一些人必須》( W. H. Freeman and Company 1974 )中講述了這樣一個故事:他在課堂上嘗試讓他的學生記住他課程中最重要的那一部分。他和他的學生講,據(jù)說普通的英國學生僅僅能記得黑斯廷斯戰(zhàn)役發(fā)生于 1066 年。迪蒙強調(diào),如果有一個學生只記住了一點點,他(她)記住的便是 1066 這個年號。迪蒙繼續(xù)講,對于他的課堂上的學生,只有幾條核心的信息,非常有趣的是,這些信息還包括:“安眠藥最終會導致失眠。”他請求他的學生們一定記住這些核心信息,即使把課堂中討論的所有其他的內(nèi)容都忘光也可以,整個學期他都為學生反復重復這些核心信息。

在學期末,期末考試的最后一道題是“請寫下這一學期中讓你銘記一生的一件東西。”當?shù)厦砷喚淼臅r候,差點兒沒昏過去。幾乎所有的學生不約而同地寫下了“ 1066 ”。

因此,我“誠惶誠恐”地向各位講述 C++ 面向?qū)ο缶幊讨凶顬橹匾囊粭l原則:公共繼承意味著“ A 是一個 B ”關系。這條原則一定要銘記在心。

如果你編寫了 B 類( Base ,基類),并編寫了由其派生出的 D 類( Derived ,派生類),那么你就告訴了 C++ 編譯器(以及代碼的讀者),每一個 D 類型的對象同時也是 B 類型的,但是反過來不成立。我們說 B 表示比 D 更加一般化的內(nèi)容,而 D 則表示比 B 更加具體化的內(nèi)容。另外我們還強調(diào)如果一個 B 類型的對象可以在某處使用時,那么 D 類型的對象一定可以在此使用。這是因為 D 類型的每個對象一定是 B 類型的。反之,如果某一刻你需要一個 D 類型的對象,那么一個 B 類型的對象則不一定能滿足要求:每個 D 都是一個 B ,但反之不然。

C++ 嚴格按上述方式解釋公共繼承。請參見下面的示例:

class Person {...};

 

class Student: public Person {...};

我們從生活的經(jīng)驗中可以得知:每個學生都是一個人,但是并不是每個人都是學生。上面的代碼正體現(xiàn)了這一層次結構。我們期望“人”的每一條屬性對“學生”都適用(比如一個人有他的出生日期,學生也有)。但是對學生能成立的屬性對于一般的人來說并不一定成立(比如一個學生被某所大學錄取了,但不是每個人都會去上大學)。人的概念比學生的概念更加寬泛,而學生是一類特殊的人。

C++ 領域中,一切需要使用 Person 類型參數(shù)的函數(shù)同樣能夠接受 Student 對象(或指向 Student 的指針或引用):

void eat(const Person& p);             // 人人都會吃飯

 

void study(const Student& s);          // 只有學生會學習

 

Person p;                              // p 是一個人

Student s;                             // s 是一個學生

 

eat(p);                                // 正確, p 是一個人

 

eat(s);                                // 正確 , s 是一個學生,

                                       // 同時一個學生是一個人

 

study(s);                              // 正確

 

study(p);                              // 錯誤! p 不一定是學生

這一點僅僅在公共繼承的情況下成立。只有在 Student 是公有派生自 Person 類時, C++ 才會按剛才米阿術的情景運行。私有繼承則意味著派生出的某些內(nèi)容是全新的。受保護的繼承今天暫且不談。

公共繼承和“ A 是一個 B ”的等價性聽上去很簡單,但是某些時候你的直覺會誤導你。比如說,一只企鵝是一只鳥,這是千真萬確的,同只鳥會飛,這是不爭的事實。如果我們在 C++ 中如此幼稚地表述這一情景,那么我們將得到:

class Bird {

public:

 virtual void fly();                  // 鳥類可以飛行

 

 ...

};

 

class Penguin:public Bird {            // 企鵝是鳥

 ...

};

瞬間我們陷入泥潭,因為這一層次結構中,企鵝竟然會飛!這顯然是荒謬的。那么問題出在哪里呢?

這種情況下,我們成為了一種不精確的語言 —— 英語的受害者。當我們說“ 鳥類能夠飛行”時,我們的意思并不是說所有的鳥類都會飛。在一般情況下,只有擁有飛行能力的鳥類才能夠飛。假如我們的語言更加精確些,我們就能認識到世界上還存在著一些不會飛的鳥類,我們也就能構建出下面的層次結構,這樣的機構才更加貼近真實世界:

class Bird {

 ...                                  // 不聲明任何飛行函數(shù)

};

 

class FlyingBird: public Bird {

public:

 virtual void fly();

 ...

};

 

class Penguin: public Bird {

 

 ...                                   // 不聲明任何飛行函數(shù)

 

};

這一層次結構比原先設計的更加忠實于我們所了解的世界。

到目前為止,上文的飛禽問題尚未徹底明了,因為在一些軟件系統(tǒng)中,區(qū)分鳥類是否可以飛行這項工作是沒有意義的,如果你的程序主要是關于鳥類的喙和翅膀,而與飛行沒有什么關系,那么原先的 2 個類的層次結構就可以滿足要求了。這里也很清晰的反映出了這一哲理:凡事并不存在一勞永逸的解決方案。對軟件系統(tǒng)而言,最好的設計一定會考慮到這個系統(tǒng)是用來做什么的,無論是現(xiàn)在還是未來,如果你的程序?qū)︼w行的問題一無所知,并且也不準備去了解,那么忽略飛行特性的設計方案很可能就是完美的。事實上,這樣做要比將兩者區(qū)分開的設計方案更好些,因為你正在模擬的世界中很可能不會存在這一機制。

對于解決上文中的“白馬非馬”的問題,還存在另外一個思考方法。那就是為企鵝重新定義 fly 函數(shù),從而讓其產(chǎn)生一個運行是錯誤:

void error(const std::string& msg);    // 定義的內(nèi)容在其他地方

 

class Penguin: public Bird {

public:

 virtual void fly() { error(" 嘗試讓一只 企鵝飛行! ”);}

 

 ...

 

};

一定要認識到:這樣做不一定能達到預期效果。因為這并不是說“企鵝不會飛”,而是說“企鵝會飛,但是在它嘗試飛行時出錯了”。

如何找出兩者的區(qū)別呢?我們從捕獲錯誤的時機入手:“企鵝不能飛”的指令可以由編譯器做出保證,但是對于“企鵝真正嘗試飛行是一個錯誤”這一規(guī)則的違背只能夠在運行時捕獲。

為了表達這一契約,“企鵝不能飛行——句號”,你要確認企鵝對象一定沒有飛行函數(shù)定義:

class Bird {

 

 ...                                  // 不聲明任何飛行函數(shù)

 

};

 

class Penguin: public Bird {

 

 ...                                  // 不聲明任何飛行函數(shù)

 

};

現(xiàn)在,如果你嘗試讓一個企鵝飛行,那么編譯器將對你的侵犯行為做出抗議:

Penguin p;

 

p.fly();                               / 錯誤!

如果你適應了“產(chǎn)生運行時錯誤”的方法,上文代碼的行為則與你所了解的大相徑庭。使用上文中的方法,編譯器不會對 p.fly 的調(diào)用做出任何反應。第 18 條中解釋了好的接口設計能夠防止非法代碼得到編譯。因此你最好使用在編譯室拒絕企鵝嘗試飛行的設計方案,而不是僅僅在運行時捕獲錯誤。

可能你承認你的鳥類學知識并不豐富,但對于初級幾何你還是有信心的吧,讓我們拿長方形和正方形再舉一個例子,這沒有什么復雜的吧?

好的,讓我們回答一個簡單的問題: Square (正方形)類是否應該公共繼承自 Rectangle 長方形)類?

 

Untitled.jpg

你會說:“當然可以了!正方形就是一個長方形,這地球人都知道。但反過來就不成立了。”這一點至少在學校里是正確的,但我們都已經(jīng)不是小學生了,請考慮下面的代碼:

class Rectangle {

public:

 virtual void setHeight(int newHeight);

 virtual void setWidth(int newWidth);

 

 virtual int height() const;          // 返回當前值

 virtual int width() const;

 

 ...

 

};

 

void makeBigger(Rectangle& r)          // 增加 r 面積的函數(shù)

{

 int oldHeight = r.height();

 

 r.setWidth(r.width() + 10);          // r 的寬增加 10

 

 assert(r.height() == oldHeight);     // 斷言 r 的高不變

}

顯然地,這里的判斷永遠不會失敗, makeBigger 僅僅改變了 r 的寬,它 的高始終沒有改變。

現(xiàn)在請觀察下面的代碼,其中使用了公共繼承,從而使得正方形得到與長方形一致的待遇:

class Square: public Rectangle {...};

 

Square s;

 

...

 

assert(s.width() == s.height());       // 這對所有的正方形都成立

 

makeBigger(s);                         // 根據(jù)繼承關系, s 是一個長方形

                                       // 因此我們可以增加它的面積

 

assert(s.width() == s.height());       // 對所有的正方形也應成立

第二次判斷同樣不應該出錯,這也是十分明顯的。因為正方形的定義要求長寬值永遠相等。

但是現(xiàn)在我們又遇到了一個問題,我們?nèi)绾谓鉀Q下面的沖突呢?

在調(diào)用 makeBigger 之前, s 的高與寬相等;

makeBigger 內(nèi)部, s 的寬值該變了,但是高沒有;

makerBigger 返回后, s 的高與寬又相等了。(請注意: s 是通過引用傳入 makeBigger 中的,因此 makeBigger 改變的是 s 本身,而不是 s 的副本。)

歡迎來到公共繼承的美妙世界。在這里,你在其他領域(包括數(shù)學)所積累的經(jīng)驗也許不會按部就班地奏效。這種情況下最基本的問題就是:一些對長方形可用的屬性(它的長、寬可以分別修改),對于正方形而言并不適用(它的長、寬必須保持一致)。但是,公共繼承要求對基類成立的一切屬性對于派生類同樣應該能夠成立——一切屬性!這種情況下,長方形和正方形(以及第 38 條中集合、線性表)的實例都會遇到問題。因此,使用公共繼承來構建它們之間的關系顯然是錯誤的。編譯器會允許你這樣做,但是就像我們剛剛所看到的一樣,我們無法確保代碼是否能夠按要求運行。這件事每一位程序員一定深有體會(往往比其他行業(yè)的人要深得多),這是因為許多情況下代碼能夠通過編譯,卻并不意味著它能夠正常運行。

在我們擁入面向?qū)ο蟪绦蛟O計的懷抱時,多年積累的編程經(jīng)驗難道成為了我們的絆腳石嗎?這一點你無需顧慮。舊有的知識依然是寶貴的,只是既然你已經(jīng)把繼承的概念添加進你大腦中的設計方案庫中,你就應該以全新的眼光來開拓自己的感官世界,從而使你在面對包含繼承的程序時不會迷失方向。假如有人向你展示了一個幾頁長的程序,你也可以從企鵝繼承自鳥類、正方形繼承自長方形這些示例所包含的理念中,找出同樣有趣的東西。這有可能是完成工作的正確途徑,只是這個可能性并不大。

類間的關系并不僅限于“ A 是一個 B ”關系。另外還存在兩個內(nèi)部類關系,它們是:“ A 擁有一個 B ”、“ A 是以 B 的形式實現(xiàn)的”。這些關系將在第 38 條和第 39 條中講解。由于人們往往會將上面兩種關系其中之一錯誤地構造成“ A 是一個 B ”,因此隨之帶來的 C++ 設計錯誤比比皆是,所以你應該確保對于這些關系之間的區(qū)別有著充分的理解,只有這樣你才能在 C++ 中分別對這些關系做出最優(yōu)秀的構造。

銘記在心

公共繼承意味著 “A 是一個 B” 的關系。對于基類成立的一切都應該適用于派生類,因為派生類的對象就是一個基類對象。

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产亚洲欧洲997久久综合| 欧美国产第一页| 久久先锋资源| 久久久精品国产99久久精品芒果| 欧美久久久久免费| 亚洲精品黄色| 亚洲精选国产| 欧美日精品一区视频| 在线视频亚洲欧美| 欧美一级在线亚洲天堂| 狠狠久久婷婷| 欧美韩日亚洲| 亚洲欧美偷拍卡通变态| 麻豆精品视频在线观看| 国产精品拍天天在线| 久久久欧美精品| 香蕉免费一区二区三区在线观看| 亚洲第一网站| 亚洲黄色天堂| 欧美日韩中文字幕在线视频| 亚洲国产精品999| 麻豆精品在线观看| 国产亚洲精品自拍| 六月婷婷久久| 欧美日韩国产区一| 欧美一区在线看| 欧美一区二区三区在线视频| 91久久国产精品91久久性色| 久久久久久久久综合| 日韩亚洲一区二区| 欧美精品一区二区三区一线天视频 | 亚洲视频精选在线| 激情综合久久| 国产精品成人免费精品自在线观看| 午夜免费在线观看精品视频| 亚洲激情欧美| 男女精品网站| 欧美激情精品久久久久久黑人 | 91久久精品国产91性色| 欧美日韩在线视频观看| 欧美刺激午夜性久久久久久久| 欧美呦呦网站| 免费亚洲一区| 久久久久久久国产| 欧美一区二区私人影院日本| 国产日韩精品在线播放| 欧美人在线观看| 欧美精品福利在线| 欧美日韩福利视频| 亚洲精品美女免费| 99精品国产99久久久久久福利| 91久久精品日日躁夜夜躁欧美| 亚洲国产精品一区制服丝袜 | 久久福利影视| 亚洲国产精品va在线观看黑人| 欧美激情视频一区二区三区在线播放 | 午夜伦理片一区| 亚洲午夜影视影院在线观看| 欧美精品999| 欧美精品激情| 欧美午夜激情视频| 国产日韩欧美日韩大片| 国产精品一级在线| 欧美日韩精品在线视频| 欧美国产先锋| 欧美偷拍另类| 国产精品最新自拍| 国内精品嫩模av私拍在线观看 | 久久精品91久久久久久再现| 亚洲人体一区| 一区二区三区精密机械公司| 欧美在线播放一区| 久久久成人精品| 欧美日韩高清区| 精品91在线| 欧美在线播放一区二区| 亚洲大片av| 性一交一乱一区二区洋洋av| 欧美日韩理论| 一区二区日本视频| 免费看黄裸体一级大秀欧美| 亚洲深夜福利在线| 欧美日韩在线播放三区| 黑人中文字幕一区二区三区| 国产综合色一区二区三区| 亚洲欧美bt| 日韩午夜精品| 欧美美女视频| 亚洲国产精品激情在线观看 | 久久精品123| 亚洲欧洲一区| 欧美极品在线视频| 精品成人国产| 免费高清在线视频一区·| 亚洲视频一二三| 欧美性片在线观看| 国产精品试看| 久久成人免费电影| 亚洲中午字幕| 亚洲精品久久久久久一区二区| 久久国产精品电影| 国产精品一区久久| 99天天综合性| 日韩视频第一页| 男男成人高潮片免费网站| 激情综合电影网| 欧美 日韩 国产精品免费观看| 久久久国产一区二区| 激情欧美丁香| 亚洲久色影视| 国产亚洲电影| 久久另类ts人妖一区二区| 久久综合导航| 国产精品一二| 免费日韩一区二区| 欧美精品一区在线播放| 在线亚洲一区| 久久国产66| 亚洲欧美日韩国产精品| 久久亚洲电影| 久久综合五月| 欧美日韩午夜| 欧美激情二区三区| 美女诱惑一区| 久久精品视频网| 国产精品乱码妇女bbbb| 欧美不卡在线视频| 黄色成人在线| 欧美一级电影久久| 精品69视频一区二区三区| 亚洲免费影视| 午夜精品三级视频福利| 欧美三日本三级少妇三2023| 亚洲国产毛片完整版| 亚洲二区在线| 久色婷婷小香蕉久久| 久久国产精品亚洲va麻豆| 国产精品99一区| 国产精品亚洲不卡a| av成人激情| 亚洲欧美另类在线观看| 欧美日韩视频专区在线播放| 亚洲夜晚福利在线观看| 欧美片第一页| 亚洲伊人伊色伊影伊综合网 | 亚洲成色999久久网站| 亚洲国产裸拍裸体视频在线观看乱了中文| 久久国产精品久久久| 欧美黄污视频| 亚洲一级电影| 在线成人免费视频| 欧美网站在线观看| 亚洲欧美国内爽妇网| 欧美14一18处毛片| 亚洲少妇诱惑| 国产主播一区二区三区| 欧美日韩亚洲在线| 久久久国产精品一区二区中文 | 欧美成年人视频| 亚洲少妇诱惑| 最新成人在线| 老司机aⅴ在线精品导航| 亚洲在线观看视频网站| 国产香蕉久久精品综合网| 免费在线亚洲欧美| 午夜久久久久久久久久一区二区| 久热这里只精品99re8久| 亚洲无人区一区| 精品99一区二区| 精品成人久久| 国产精品美女久久久| 亚洲欧美不卡| 亚洲毛片在线观看.| 国产伦精品一区二区三区免费迷 | 91久久黄色| 欧美日韩不卡| 久久久久久黄| 亚洲影视在线播放| 亚洲手机在线| 亚洲精品中文字| 亚洲免费观看在线观看| 欧美福利在线观看| 这里是久久伊人| 亚洲一区二区三区在线观看视频| 亚洲成人自拍视频| 亚洲一区二区三区四区五区黄| 男男成人高潮片免费网站| 欧美激情在线播放| 久久精品夜色噜噜亚洲a∨| 亚洲欧美一区二区精品久久久| 午夜亚洲视频| 欧美一区二区三区免费看| 午夜久久一区| 美女视频黄 久久| 你懂的视频一区二区| 亚洲日韩视频| 在线视频你懂得一区| 蘑菇福利视频一区播放| 亚洲国产一二三| 99天天综合性|