【正文】
在(非常簡化的)書店定。但是,在繼承層次中定義類還需要另外一些特性,本節(jié)將介紹這些特性,后續(xù)的章節(jié)將介紹這些特性的使用對類以及使用繼承類編寫的程序有何影響。用引用(或指針)調(diào)用的虛函數(shù)在運(yùn)行時(shí)確定,被調(diào)用的函數(shù)是引用(或指針)所指對象的實(shí)際類型所定義的。 在 C++ 中,通過基類的引用(或指針)調(diào)用虛函數(shù)時(shí),發(fā)生動(dòng)態(tài)綁定。調(diào)用哪個(gè)版本的 _price 將依賴于傳給 print_total 的實(shí)參。 第一,雖然這個(gè)函數(shù)的第二形參是 Item_base 的引用但可以將 Item_base 對象或 Bulk_item 對象傳給它。 } 該函數(shù)的工作很普通:調(diào)用其 item 形參的 book 和 _price 函數(shù),打印結(jié)果。os, const Item_base amp。給定一個(gè)項(xiàng)目和數(shù)量,函數(shù)應(yīng)打印 ISBN 以及購買給定數(shù)量的某 書的總價(jià)。 例如,書店應(yīng)用程序可以允許顧客在一次交易中選擇幾本書,當(dāng)顧客購書時(shí),應(yīng)用程序可以計(jì)算總的應(yīng)付款,指出最終賬單的一個(gè)部分將是為每本書打印一行,以顯示總數(shù)和售價(jià)。 動(dòng)態(tài)綁定 我們能夠編寫程序使用繼承層次中任意類型的對象,無須關(guān)心對象的具體類型。討論過這些之后,可以看到我們的類將定義三個(gè)( const)成員函數(shù) :虛函數(shù) double _price(size_t) 的兩個(gè)版本,返回給定數(shù)目的某書的總價(jià)。另一方面,每個(gè)派生類需要定義自己的 _price 函數(shù)版本,以實(shí)現(xiàn)適當(dāng)?shù)恼劭蹆r(jià)格策略。 名為 _price 的操作,返回購買指定數(shù)量的書的價(jià)格。在書店例子中,我們將定義一個(gè)基類,命名為 Item_base,命名為 Bulk_item,表示帶數(shù)量折扣銷售的書。 我們經(jīng)常稱因繼承而相關(guān)聯(lián)的類為構(gòu)成了一個(gè) 繼承層次 。派生類( derived class)能夠繼承基類( base class)定義的成員,派生類可以無須改變而使用那些與派生類型具體特性不相關(guān)的操作,派生類可以重定義那些與派生類型相關(guān)的成員函數(shù),將函數(shù)特化,考慮派生類型的特性。正如我們將看到的,在 C++ 中,多態(tài)性僅用于通過繼承而相關(guān)聯(lián)的類型的引用或指針。多態(tài)性派生于一個(gè)希臘單詞,意思是 “ 許多形態(tài) ” 。在討論 C++ 如何支持面向?qū)ο缶幊讨?,我們將介紹這種編程風(fēng)格的一些基本概念。通過繼承可以定義一些類型,以模擬不同種類的書,通過動(dòng)態(tài)綁定可以編寫程序,使用這些類型而又忽略與具體類型相關(guān)的差異??梢越o購買某書一定數(shù)量的顧客打折,或者,購買一定數(shù)量以內(nèi)可以打折而超過給定限制就付全價(jià)。 許多應(yīng)用程序 的特性可以用一些相關(guān)但略有不同的概念來描述。動(dòng)態(tài)綁定使編譯器能夠在運(yùn)行時(shí)決定是使用基類中定義的函數(shù)還是派生類中定義的函數(shù)。 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)綁定。 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 }。d like our users to be able to specify values for the entire Bulk_item, including the discount rate and quantity. The constructor initializer list for a derivedclass constructor may initialize only the members of the derived class。 This constructor uses the constructor initializer list to initialize its min_qty and discount members. The constructor initializer also implicitly invokes the Item_base default constructor to initialize its baseclass part. The effect of running this constructor is that first the Item_base part is initialized using the Item_base default constructor. That constructor sets isbn to the empty string and price to zero. After the Item_base constructor finishes, the members of the Bulk_item part are initialized, and the (empty) body of the constructor is executed. Passing Arguments to a BaseClass Constructor In addition to the default constructor, our Item_base class lets users initialize the isbn and price members. We39。book = , double sales_price = ): isbn(book), price(sales_price) { } The only impact inheritance has on baseclass constructors is that there is a new kind of user that must be considered when deciding which constructors to offer. Like any other member, constructors can be made protected or private. Some classes need special constructors that are intended only for their derived classes to use. Such constructors should be made protected. protected。 each class defines its own constructor(s) and copycontrol members. As is the case for any class, synthesized versions of the default constructor and the copycontrol members will be used if the class does not define its own versions. BaseClass Constructors and Copy Control Constructors and copy control for base classes that are not themselves a derived class are largely unaffected by inheritance. Our Item_base constructor looks like many we39。 it has the same interface as its base class. In welldesigned class hierarchies, objects of a publicly derived class can be used wherever an object of the base class is expected. Classes derived using either private or protected do not inherit the baseclass inter