c primer閱讀筆記 15章 5

2021-06-21 16:31:18 字數 2883 閱讀 8431

15.5. 繼承情況下的類作用域

1、在繼承情況下,派生類的作用域巢狀在基類作用域中。如果不能在派生類作用域中確定名字,就在外圍基類作用域中查詢該名字的定義。正是這種類作用域的層次巢狀使我們能夠直接訪問基類的成員,就好象這些成員是派生類成員一樣。

15.5.1. 名字查詢在編譯時發生

1、物件、引用或指標的靜態型別(???)決定了物件能夠完成的行為。甚至當靜態型別和動態型別可能不同的時候,就像使用基類型別的引用或指標時可能會發生的,靜態型別仍然決定著可以使用什麼成員。例如,可以給 disc_item 類增加乙個成員,該成員返回乙個儲存最小(或最大)數量和折扣**的 pair 物件:

class disc_item : public item_base 

// other members as before

};

只能通過 disc_item 型別或 disc_item 派生型別的物件、指標或引用訪問 discount_policy:

bulk_item bulk;

bulk_item *bulkp = &bulk; // ok: static and dynamic types are the same

item_base *itemp = &bulk; // ok: static and dynamic types differ

bulkp->discount_policy(); // ok: bulkp has type bulk_item*

itemp->discount_policy(); // error: itemp has type item_base*

重新定義 itemp 的訪問是錯誤的,因為基類型別的指標(引用或物件)只能訪問物件的基類部分,而在基類中沒有定義 discount_policy 成員。

15.5.2. 名字衝突與繼承

1、與基類成員同名的派生類成員將遮蔽對基類成員的直接訪問。

2、可以使用作用域操作符訪問被遮蔽的基類成員。

15.5.3. 作用域與成員函式

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

2、區域性作用域中宣告的函式不會過載全域性作用域中定義的函式(第 7.8.1 節),同樣,派生類中定義的函式也不過載基類中定義的成員。通過派生類物件呼叫函式時,實參必須與派生類中定義的版本相匹配,只有在派生類根本沒有定義該函式時,才考慮基類函式。

過載函式

3、像其他任意函式一樣,成員函式(無論虛還是非虛)也可以過載。派生類可以重定義所繼承的 0 個或多個版本。

4、如果派生類重定義了過載成員,則通過派生型別只能訪問派生類中重定義的那些成員。

5、如果派生類想通過自身型別使用的過載版本,則派生類必須要麼重定義所有過載版本,要麼乙個也不重定義。

6、派生類不用重定義所繼承的每乙個基類版本,它可以為過載成員提供 using 宣告(第 15.2.5 節)。乙個 using 宣告只能指定乙個名字,不能指定形參表,因此,為基類成員函式名稱而作的 using 宣告將該函式的所有過載例項加到派生類的作用域。將所有名字加入作用域之後,派生類只需要重定義本型別確實必須定義的那些函式,對其他版本可以使用繼承的定義。

15.5.4. 虛函式與作用域

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

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

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 中定義的非虛版本。

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

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

base bobj;  d1 d1obj;  d2 d2obj;

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

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

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

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

三個指標都是基類型別的指標,因此通過在 base 中查詢 fcn 來確定這三個呼叫,所以這些呼叫是合法的。另外,因為 fcn 是虛函式,所以編譯器會生成**,在執行時基於引用指標所繫結的物件的實際型別進行呼叫。在 bp2 的情況,基本物件是 d1 類的,d1 類沒有重定義不接受實參的虛函式版本,通過 bp2 的函式呼叫(在執行時)呼叫 base 中定義的版本。

c primer閱讀筆記 15章 1

1 物件導向程式設計基於三個基本概念 資料抽象 繼承和動態繫結。在 c 中,用類進行資料抽象,用類派生從乙個類繼承另乙個 派生類繼承基類的成員。動態繫結使編譯器能夠在執行時決定是使用基類中定義的函式還是派生類中定義的函式。15.1.物件導向程式設計 概述 1 物件導向程式設計的關鍵思想是多型性 po...

c primer閱讀筆記 15章 4

15.4.建構函式和複製控制 1 建構函式和複製控制成員不能繼承,每個類定義自己的建構函式和複製控制成員。15.4.1.基類建構函式和複製控制 1 本身不是派生類的基類,其建構函式和複製控制基本上不受繼承影響。15.4.2.派生類建構函式 1 派生類的建構函式受繼承關係的影響,每個派生類建構函式除了...

c primer閱讀筆記 15章 7

15.8.控制代碼類與繼承 1 c 中物件導向程式設計的乙個頗具諷刺意味的地方是,不能使用物件支援物件導向程式設計,相反,必須使用指標或引用。2 但是,使用指標或引用會加重類使用者的負擔。3 c 中乙個通用的技術是定義包裝 cover 類或控制代碼類。控制代碼類儲存和管理基類指標。指標所指物件的型別...