1.什麼是虛成員函式
即其宣告在返回型別的前面帶有關鍵字virtual的類成員函式。定義為virtual的函式是基類期待派生類重新定義的,基類希望派生類繼承的函式不能定義為虛函式。
2.動態繫結
通過動態繫結,我們能夠編寫程式使用繼承層次中任意型別的物件,無須關心物件的具體型別。
在c++中,通過基類的引用(或指標)呼叫虛函式時,發生動態繫結。引用(或指標)既可以指向基類物件也可以指向派生類物件,這一事實是動態繫結的關鍵。
用引用(或指標)呼叫的虛函式在執行時確定,被呼叫的函式是引用(或指標)所指物件的實際型別所定義的。
3.基類成員函式
成員預設為非虛函式,對非虛函式的呼叫在編譯時確定。為了指明函式為虛函式,在其返回型別的前面加上virtual。除建構函式之外,任意非static成員函式都可以是虛函式。保留字virtual只在類內部的成員函式中宣告,不能用在類定義體外部出現的函式定義上。
4.派生類與虛函式
儘管不是必須這樣做,派生類一般會重新定義所繼承的虛函式。如果派生類沒有重新定義虛函式,則使用基類中定義的版本。派生型別必須對想要重新定義的每個繼承成員進行宣告。派生類中虛函式的宣告必須與基類中的定義方式一樣,但有乙個例外:返回基型別的引用(或指標)的虛函式。派生類的虛函式可以返回基類函式所返回型別的派生類的引用(或指標)。
注意:一旦函式在基類宣告中為虛函式,它就一直為虛函式,派生類無法改變該函式為虛函式這一事實。派生類重定義虛函式時,可以使用virtual保留字,但不是必須這樣做。
5.virtual與其他成員函式
要觸發動態繫結,必須滿足兩個條件:第一,只有指定為虛函式的成員函式才能進行動態繫結,成員函式預設為非虛函式,非虛函式不進行動態繫結;第二,必須通過基類型別的引用或指標進行函式呼叫。
1)從派生類到基類的轉換
因為每個派生類物件都包含基類部分,所以可將基類型別的引用繫結到派生類物件的基類部分,也可以將指向基類的指標指向派生類物件。
使用基類型別的指標和引用時,不知道指標和引用所繫結的物件的型別:基類型別的引用或指標既可以引用基類型別物件,又可以引用派生型別物件。無論實際物件是哪一種,編譯器都將它當作基類型別物件。(將派生類物件當作基類物件是安全的,因為每個派生類物件都擁有基類子物件。而且,派生類繼承基類的操作,即任何作用於基類物件上執行的操作也可以通過派生類物件使用)。
注:基類型別引用和指標的關鍵點在於靜態型別和動態型別可能不同。靜態型別,在編譯時可知的引用型別或指標型別;動態型別,指標或引用型別繫結的物件的型別,這僅僅是執行時可知。
2)可以在執行時確定virtual函式的呼叫
c++動態繫結的關鍵:物件的實際型別(動態型別)可能不同於該物件的引用或指標的靜態型別。
3)在編譯時確定非virtual函式的呼叫
非虛函式總是在編譯時根據呼叫該函式的物件、引用、指標的型別而確定。
4)覆蓋虛函式機制
在某些情況下,希望覆蓋虛函式機制並強制函式呼叫使用虛函式的特定版本,這是可以使用作用域操作符。
如item_base *basep = &derived; basep->item_base::net_price(42);//該呼叫編譯時確定(如果沒有作用域則在執行時呼叫basep所指向物件相對應的函式)
使用覆蓋虛函式機制的理由是為了派生類虛函式呼叫基類中的版本。這種情況下,基類版本可以完成基礎層次中所有型別的公共任務,而每個派生型別只新增自己的特殊工作。
派生類虛函式呼叫基類版本時,必須顯式使用作用域操作符。如果派生類函式忽略了這樣做,則函式呼叫會在執行時確定並且將會是乙個自身呼叫,從而導致無窮遞迴。
例子:基類item_base的定義(item_base.h)
1 #ifndef item_base_h
2 #define item_base_h
3 #include
4 class item_base
7 std::string book() const /*返回書的編號*/
8 virtual double net_price(std::size_t n) const /*返回給定數目的某書的總價(實價)*/
9
12 virtual ~item_base()
13 private:
14 std::string isbn;
15 protected:
16 double price; /*price為protected,可以被派生類物件訪問但不能被該型別的普通使用者訪問*/
17 };
18 #endif
派生類 bulk_item(bulk_item.h)
1 #ifndef bulk_item_h
2 #define bulk_item_h
3 #include "item_base.h"
4 5 class bulk_item:public item_base
6 11 double net_price(std::size_t n) const;
12 private:
13 std::size_t min_qty; //折扣的最小購買數量
14 double discount; //折扣
15 };
16 #endif
派生類實現bulk_item.cpp
1 #include
2 #include "bulk_item.h"
3 using namespace std;
4 5 double bulk_item::net_price(size_t n) const
6 12
13 void print_total(ostream &os, const item_base &item, size_t n)
14 測試test.cpp
1 #include
2 #include "item_base.h"
3 #include "bulk_item.h"
4 using namespace std;
5 6 int main()
7 測試結果:27.6
使用基類item_base的引用呼叫虛成員函式會發生動態繫結,被呼叫的函式是與動態型別即bulk_item相對應的函式。
使用作用域操作符覆蓋虛函式機制,即item_base::可以呼叫基類的版本。
虛函式和動態繫結 C 學習
1.什麼是虛成員函式 即其宣告在返回型別的前面帶有關鍵字virtual的類成員函式。定義為virtual的函式是基類期待派生類重新定義的,基類希望派生類繼承的函式不能定義為虛函式。2.動態繫結 通過動態繫結,我們能夠編寫程式使用繼承層次中任意型別的物件,無須關心物件的具體型別。在c 中,通過基類的引...
c 規避虛函式的動態繫結
如果我們希望明確指定呼叫 基類 還是 子類的虛函式版本。那麼可以通過作用域運算子 注 這一特性是設計模式中 責任鏈模式 的核心實現手段。class a class b public a int tmain int argc,tchar ar 這個特性非常有用,當很多派生類 都用相同動作時,我們可以把...
虛函式與動態繫結
在定義基類時,我們希望基類中的有些函式可以在派生類中重新定義。比如,我們定義了基類記錄的書,可以求出買了多少書花了多少錢 而在派生類中,我們定義的是打折的書,還是要計算買了多少書花了多少錢。這時,就需要重新定義計算錢數的函式了。注意,這裡的重新定義,與之前講過的函式過載或者操作符過載不同 後面兩類,...