classb ;class d: public b ;
甚至對b,d或mf一無所知,也可以定義乙個型別d的物件x,
d x; // x是型別d的乙個物件
那麼,如果發現這麼做:
b *pb = &x; // 得到x的指標
pb->mf(); // 通過指標呼叫mf
和下面這麼做的執行行為不一樣:
d *pd = &x; // 得到x的指標
pd->mf(); // 通過指標呼叫mf
你一定就會感到很驚奇。
如果mf是非虛函式而d又定義了自己的mf版本,行為就不會相同:
class d: publicb ;pb->mf(); //
呼叫b::mf
pd->mf(); //
呼叫d::mf
名字查詢與繼承:(函式呼叫步驟)
假設呼叫p->mem()
1.首先確定p的靜態型別
2.在p的靜態型別對應的類中查詢,如果找不到,則依次在直接基類中不斷查詢直至到達繼承鏈的頂端,如果找遍了該類及其基類仍然找不到,編譯器將報錯
3.一旦找到mem,就進行常規的型別檢查,以確認對於當前找到的mem,本次呼叫是否合法
4.假設呼叫合法,則編譯器根據呼叫的是否是虛函式而產生不同的**:
如果mem是虛函式且我們是通過引用或指標進行的呼叫,則編譯器產生的**將在執行時確定到底執行該虛函式的哪個版本,依據是物件的動態型別;
反之,則編譯器將產生乙個常規函式呼叫,即靜態繫結
條款37 絕不要重新定義繼承而來的預設引數值
總結 不要重新定義乙個繼承而來的預設引數值,因為 預設引數值是靜態繫結 而virtual函式 你唯一應該覆寫的東西 是動態繫結。我們應該知道,virtual函式是動態繫結 dynamically bound 預設引數值卻是靜態繫結 statically bound 物件的靜態型別 static ty...
決不要重新定義繼承而來的非虛函式
有兩種方法來看待這個問題 理論的方法和實踐的方法。讓我們先從實踐的方法開始。畢竟,理論家一般都很耐心。假設類d公有繼承於類b,並且類b中定義了乙個公有成員函式mf。mf的引數和返回型別不重要,所以假設都為void。換句話說,我這麼寫 class b class d public b 甚至對b,d或m...
條款37 絕不重新定義繼承而來的預設引數值
條款37 絕不重新定義繼承而來的預設引數值 靜態繫結就是在程式中被宣告時所採用的型別 includeusing namespace std class shape virtual void draw shapecolor color red const 0 class rectangle publi...