【正文】
lizes the members it inherits by including its base class in its constructor initializer list: class Bulk_item : public Item_base { public: Bulk_item(const std::stringamp。 book, double sales_price, std::size_t qty = 0, double disc_rate = ): Item_base(book, sales_price), min_qty(qty), discount(disc_rate) { } // as before }。 This constructor uses the twoparameter Item_base constructor to initialize its base subobject. It passes its own book and sales_price arguments to that constructor. We might use this constructor as follows: // arguments are the isbn, price, minimum quantity, and discount Bulk_item bulk(0201824701, 50, 5, .19)。 bulk is built by first running the Item_base constructor, which initializes isbn and price from the arguments passed in the Bulk_item constructor initializer. After the Item_base constructor finishes, the members of Bulk_item are initialized. Finally, the (empty) body of the Bulk_item constructor is run. item Using Default Arguments in a Derived Constructor Of course, we might write these two Bulk_item constructors as a single constructor that takes default arguments: class Bulk_item : public Item_base { public: Bulk_item(const std::stringamp。 book, double sales_price, std::size_t qty = 0, double disc_rate = ): Item_base(book, sales_price), min_qty(qty), discount(disc_rate) { } // as before }。 Here we provide defaults for each parameter so that the constructor might be used with zero to four arguments. Only an Immediate Base Class May Be Initialized A class may initialize only its own immediate base class. An immediate base class is the class named in the derivation list. If class C is derived from class B, which is derived from class A, then B is the immediate base of C. Even though every C object contains an A part, the constructors for C may not initialize the A part directly. Instead, class C initializes B, and the constructor for class B in turn initializes A. The reason for this restriction is that the author of class B has specified how to construct and initialize objects of type B. As with any user of class B, the author of class C has no right to change that specification. As a more concrete example, our bookstore might have several discount strategies. In addition to a bulk discount, it might offer a discount for purchases up to a certain quantity and then charge the full price thereafter. Or it might offer a discount for purchases above a certain limit but not for purchases up to that limit. Each of these discount strategies is the same in that it requires a quantity and a discount amount. We might support these differing strategies by defining a new class named Disc_item to store the quantity and the discount amount. This class would not define a _price function but would serve as a base class for classes such as Bulk_item that define the different discount strategies. 面向?qū)ο蟪绦蛟O(shè)計(jì) 面向?qū)ο缶幊袒谌齻€(gè)基本概念:數(shù)據(jù)抽象、繼承和動(dòng)態(tài)綁定。在 C++ 中,用類進(jìn)行數(shù)據(jù)抽象,用類派生從一個(gè)類繼承另一個(gè):派生類繼承基類的成員。動(dòng)態(tài)綁定使編譯器能夠在運(yùn)行時(shí)決定是使用基類中定義的函數(shù)還是派生類中定義的函數(shù)。 繼承和動(dòng)態(tài)綁定在兩個(gè)方面簡(jiǎn)化了我們的程序:能夠容易地定義與其他類相似但又不相同的新類,能夠更容易地編寫忽略這些相似類型之間區(qū)別的程序。 許多應(yīng)用程序 的特性可以用一些相關(guān)但略有不同的概念來(lái)描述。例如,書店可以為不同的書提供不同的定價(jià)策略,有些書可以只按給定價(jià)格出售,另一些書可以根據(jù)不同的折扣策略出售。可以給購(gòu)買某書一定數(shù)量的顧客打折,或者,購(gòu)買一定數(shù)量以內(nèi)可以打折而超過(guò)給定限制就付全價(jià)。 面向?qū)ο缶幊蹋?Objectoriented programming, OOP)與這種應(yīng)用非常匹配。通過(guò)繼承可以定義一些類型,以模擬不同種類的書,通過(guò)動(dòng)態(tài)綁定可以編寫程序,使用這些類型而又忽略與具體類型相關(guān)的差異。 繼承和動(dòng)態(tài)綁定的思想在概念上非常簡(jiǎn)單,但對(duì)于如何創(chuàng)建應(yīng)用 程序以及對(duì)于程序設(shè)計(jì)語(yǔ)言必須支持哪些特性,它們的含義深遠(yuǎn)。在討論 C++ 如何支持面向?qū)ο缶幊讨埃覀儗⒔榻B這種編程風(fēng)格的一些基本概念。 1. 面向?qū)ο缶幊蹋焊攀? 面向?qū)ο缶幊痰年P(guān)鍵思想是多態(tài)性( polymorphism)。多態(tài)性派生于一個(gè)希臘單詞,意思是 “ 許多形態(tài) ” 。之所以稱通過(guò)繼承而相關(guān)聯(lián)的類型為多態(tài)類型,是因?yàn)樵谠S多情況下可以互換地使用派生類型或基類型的 “ 許多形態(tài) ” 。正如我們將看到的,在 C++ 中,多態(tài)性僅用于通過(guò)繼承而相關(guān)聯(lián)的類型的引用或指針。 通過(guò)繼承我們能夠定義這樣的類,它們對(duì)類 型之間的關(guān)系建模,共享公共的東西,僅僅特化本質(zhì)上不同的東西。派生類( derived class)能夠繼承基類( base class)定義的成員,派生類可以無(wú)須改變而使用那些與派生類型具體特性不相關(guān)的操作,派生類可以重定義那些與派生類型相關(guān)的成員函數(shù),將函數(shù)特化,考慮派生類型的特性。最后,除了從基類繼承的成員之外,派生類還可以定義更多的成員。 我們經(jīng)常稱因繼承而相關(guān)聯(lián)的類為構(gòu)成了一個(gè) 繼承層次 。其中有一個(gè)類稱為根,所以其他類直接或間接繼承根類。在書店例子中,我們將定義一個(gè)基類,命名為 Item_base,命名為 Bulk_item,表示帶數(shù)量折扣銷售的書。 ? 這些類至少定義如下操作: ? 名為 book 的操作,返回 ISBN。 名為 _price 的操作,返回購(gòu)買指定數(shù)量的書的價(jià)格。 Item_base 的派生類將無(wú)須改變地繼承 book 函數(shù):派生類不需要重新定義獲取 ISBN 的含義。另一方面,每個(gè)派生類需要定義自己的 _price 函數(shù)版本,以實(shí)現(xiàn)適當(dāng)?shù)恼劭蹆r(jià)格策略。 在 C++ 中,基類必須指出希望派生類重寫哪些函數(shù),定義為 virtual 的函數(shù)是基類期待派生類重新定義的,基類希望派生類繼承的函數(shù)不能定義為虛函數(shù)。討論過(guò)這些之后,可以看到我們的類將定義三個(gè)( const)成員函數(shù) :虛函數(shù) double _price(size_t) 的兩個(gè)版本,返回給定數(shù)目的某書的總價(jià)。Item_base 類和 Bulk_item 類將定義該函數(shù)自己的版本。 動(dòng)態(tài)綁定 我們能夠編寫程序使用繼承層次中任意類型的對(duì)象,無(wú)須關(guān)心對(duì)象的具體類型。使用這些類的程序無(wú)須區(qū)分函數(shù)是在基類還是在派生類中定義的。 例如,書店應(yīng)用程序可以允許顧客在一次交易中選擇幾本書,當(dāng)顧客購(gòu)書時(shí),應(yīng)用程序可以計(jì)算總的應(yīng)付款,指出最終賬單的一個(gè)部分將是為每本書打印一行,以顯示總數(shù)和售價(jià)。 可以定義一個(gè)名為 print_total 的函數(shù)管理應(yīng)用程序的這個(gè)部分。給定一個(gè)項(xiàng)目和數(shù)量,函數(shù)應(yīng)打印 ISBN 以及購(gòu)買給定數(shù)量的某 書的總價(jià)。這個(gè)函數(shù)的輸出應(yīng)該像這樣 ,可以這樣編寫 print_total 函數(shù): void print_total(ostream amp。os, const Item_base amp。item, size_t n) { os ISBN: () // calls Item_base::book \tnumber sold: n \ttotal price: // virtual call: which version of _price to call is resolved at run time (n) endl。 } 該函數(shù)的工作很普通:調(diào)用其 item 形參的 book 和 _price 函數(shù),打印結(jié)果。關(guān)于這個(gè)函數(shù),有兩 點(diǎn)值得注意。 第一,雖然這個(gè)函數(shù)的第二形參是 Item_base 的引用但可以將 Item_base 對(duì)象或 Bulk_item 對(duì)象傳給它。 第二,因?yàn)樾螀⑹且们? _price 是虛函數(shù),所以對(duì) _price 的調(diào)用將在運(yùn)行時(shí)確定。調(diào)用哪個(gè)版本的 _price 將依賴于傳給 print_total 的實(shí)參。如果傳給 print_total 的實(shí)參是一個(gè) Bulk_item 對(duì)象,將運(yùn)行 Bulk_item 中定義的應(yīng)用折扣的 _price;如果實(shí)參是一個(gè) Item_base 對(duì)象 ,則調(diào)用由 Item_base 定義的版本。 在 C++ 中,通過(guò)基類的引用(或指針)調(diào)用虛函數(shù)時(shí),發(fā)生動(dòng)態(tài)綁定。引用(或指針)既可以指向基類對(duì)象也可以指向派生類對(duì)象,這一事實(shí)是動(dòng)態(tài)綁定的關(guān)鍵。用引用(或指針)調(diào)用的虛函數(shù)在運(yùn)行時(shí)確定,被調(diào)用的函數(shù)是引用(或指針)所指對(duì)象的實(shí)際類型所定義的。 定義基類和派生類 基類和派生類的定義在許多方面像我們已見過(guò)的其他類一樣。但是,在繼承層次中定義類還需要另外一些特性,本節(jié)將介紹這些特性,后續(xù)的章節(jié)將介紹這些特性的使用對(duì)類以及使用繼承類編寫的程序有何影響。 定義基類 像任意其他類一樣,基類也有定義其接口和實(shí)現(xiàn)的數(shù)據(jù)和函數(shù)成員。在(非常簡(jiǎn)化的)書店