C 學習筆記11 物件導向2

2021-09-06 19:32:21 字數 3531 閱讀 4002

1.  僅僅能初始化直接基類

乙個類僅僅能初始化自己的直接基類。直接就是在派生列表中指定的類。假設類c 從類b 派生,類b 從類a 派生,則b 是c 的直接基類。儘管每乙個c 類物件包括乙個a 類部分,但c 的建構函式不能直接初始化a 部分。相反,須要類c 初始化類b,而類b 的建構函式再初始化類a。這一限制的原因是,類b 的作者已經指定了如何構造和初始化b 型別的物件。像類b 的不論什麼使用者一樣,類c 的作者無權改變這個規約。

2. 重構

將disc_item 加到item_base 層次是重構(refactoring)的乙個樣例。重構包含又一次定義類層次,將操作和/或資料從乙個類移到還有乙個類。為了適應應用程式的須要而又一次設計類以便新增新函式或處理其它改變時,最有可能須要進行重構。

重構常見在物件導向應用程式中很常見。值得注意的是,儘管改變了繼承層次,使用bulk_item 類或item_base 類的**不須要改變。然而,對類進行重構,或以隨意其它方式改變類,使用這些類的隨意**都必須又一次編譯。

3. 尊重基類介面

建構函式僅僅能初始化其直接基類的原因是每乙個類都定義了自己的介面。定義disc_item 時,通過定義它的建構函式指定了如何初始化disc_item 物件。一旦類定義了自己的介面,與該類物件的全部互動都應該通過該介面,即使物件是派生類物件的一部分也不例外。相同,派生類建構函式不能初始化基類的成員且不應該對基類成員賦值。假設那些成員為public 或protected,派生建構函式能夠在建構函式函式體中給基類成員賦值,可是,這樣做會違反基類的介面。派生類應通過使用基類建構函式尊重基類的初始化意圖,而不是在派生類構造函

class derived: public base 

};

假設析構函式為虛函式,那麼通過指標呼叫時,執行哪個析構函式將因指標所指物件型別的不同而不同:

item_base *itemp =new item_base; // same static and dynamic type

delete itemp; // ok:destructor for item_base called

itemp = newbulk_item; // ok: static and dynamic types differ

delete itemp;

6. 作用域與成員函式

在基類和派生類中使用同一名字的成員函式,其行為與資料成員一樣:在派生類作用域中派生類成員將遮蔽基類成員。即使函式原型不同,基類成員也會被遮蔽:

struct base ;

struct derived : base;

derived d; base b;

b.memfcn(); // calls base::memfcn

d.memfcn(10); // calls derived::memfcn

d.memfcn(); // error: memfcn with no arguments is hidden

d.base::memfcn(); // ok: calls base::memfcn

7.  虛函式與作用域

還記得嗎,要獲得動態繫結,必須通過基類的引用或指標呼叫虛成員。當我們這樣做時,編譯器將在基類中查詢函式。假定找到了名字,編譯器就檢查實參是否與形參匹配。

如今能夠理解虛函式為什麼必須在基類和派生類中擁有同一原型了。假設基類成員與派生類成員接受的實參不同,就沒有辦法通過基類型別的引用或指標呼叫派生類函式。考慮例如以下(人為的)為集合: 

class base ;

class d1 : public base;

class d2 : public d1;

d1 中的fcn 版本號沒有重定義base 的虛函式fcn,相反,它遮蔽了基類的fcn。結果d1 有兩個名為fcn 的函式:類從base 繼承了乙個名為fcn 的虛函式,類又定義了自己的名為fcn 的非虛成員函式,該函式接受乙個int 形參。可是,從base 繼承的虛函式不能通過d1 物件(或d1 的引用或指標)呼叫,由於該函式被fcn(int) 的定義遮蔽了。

類d2 重定義了它繼承的兩個函式,它重定義了base 中定義的fcn 的原始版本號並重定義了d1 中定義的非虛版本號

8. 通過基類呼叫被遮蔽的虛函式

通過基類型別的引用或指標呼叫函式時,編譯器將在基類中查詢該函式而忽略派生類:

base bobj; d1 d1obj;d2 d2obj;

base *bp1 =&bobj, *bp2 = &d1obj, *bp3 = &d2obj;

bp1->fcn(); // ok:virtual call, will call base::fcn at run time

bp2->fcn(); // ok:virtual call, will call base::fcn at run time

bp3->fcn(); // ok:virtual call, will call d2::fcn at run time

9. 名字查詢與繼承

理解c++ 中繼承層次的關鍵在於理解怎樣確定函式呼叫。確定函式呼叫遵循下面四個步驟:

1. 首先確定進行函式呼叫的物件、引用或指標的靜態型別。 

2. 在該類中查詢函式,假設找不到,就在直接基類中查詢,如此循著類的繼承鏈往上找,直到找到該函式或者查詢完最後乙個類。假設不能在類或其相關基類中找到該名字,則呼叫是錯誤的。

3. 一旦找到了該名字,就進行常規型別檢查檢視假設給定找到的定義,該函式呼叫是否合法。

4. 假定函式呼叫合法,編譯器就生成**。假設函式是虛函式且通過引用或指標呼叫,則編譯器生成**以確定依據物件的動態型別執行哪個函式版本號,否則,編譯器生成**直接呼叫函式。

將派生類物件拷貝到基類物件時,派生類物件將被切掉

10. 控制代碼類與繼承

c++ 中物件導向程式設計的乙個頗具諷刺意味的地方是,不能使用物件支援物件導向程式設計,相反,必須使用指標或引用。比如,以下的**段中:

void get_prices(item_base object,

const item_base *pointer,

const item_base &reference)

{ // which version ofnet_price is called is determined at run time

cout cout <

通過pointer 和reference 進行的呼叫在執行時依據它們所繫結物件的動態型別而確定。

C 學習筆記(物件導向)

protected 受保護的 用protected宣告的成員稱為受保護的成員,它不能被類外訪問,但是可以被派生類的成員函式訪問 在類體中,既不寫關鍵字private,又不寫public,則預設為private 宣告類的方法是由宣告結構體型別的方法發展來的 在類體中直接定義函式時,不需要在函式名前邊加...

Swift學習筆記 物件導向(2)

定義計算屬性 修飾符 var 計算屬性名 屬性型別 set 形參名 1,修飾符可省略 2.var 只能定義成變數,因此只能使用var 使用列舉定義乙個計算屬性 enum season set set newvalue var s season.spring 正在執行getter方法 春暖花開 pri...

物件導向java學習筆記2

x abc y x和y指向同乙個。x abc y abc x和y的值相同。拓 char x abc char y abc x y的結果是false,判讀的是指標引用的記憶體位址不同 x y的結果是true,兩個指標位址所儲存的數值相同。例項1 package oolearning public cl...