虛擬函式 C++
阿新 • • 發佈:2018-10-31
C++ 虛擬函式
虛擬函式
基類中使用virtual關鍵字宣告的函式,稱為虛擬函式。
虛擬函式的實現,通過虛擬函式表來實現的。即V-table 這個表中有一個類,用於儲存虛擬函式的地址。解決其繼承,覆蓋的問題,用於保證其真實反映的函式。這樣有虛擬函式的例項,將會儲存在這個例項的記憶體中。即用父類的指標,操作子類的時候,通過虛擬函式表來實現找尋到父類。
定義下方的一個類
class Base{ public: virtual void f(){ cout << "Base::f" << endl; } virtual void g(){ cout << "Base::g" << endl; } virtual void h(){ cout << "Base::h" << endl; } }
然後通過例項化得到虛擬函式表
Base* b = new Base();
//Fun pFun = NULL;
cout << "虛擬函式表地址 " << (int*)(&(*b)) << endl; // 強制轉換成為指標型別,然後輸出
cout << "虛擬函式表 - 第一個函式地址" << (int*)*(int*)(&(*b)) << endl; // 取虛擬函式的地址,然後獲得虛擬函式的頭指向的第一個儲存函式的記憶體空間。然後獲取第一個函式的記憶體地址
然後檢視輸出的結果
虛擬函式表地址 0xb31268
虛擬函式表 - 第一個函式地址0x4c1490
根據地址可以看到,指標b指向建立的例項的地址,其首地址儲存著虛擬函式表的地址,然後再次通過指標訪問,得到虛擬函式表的第一個函式的指標的地址。
一般繼承,沒有虛擬函式覆蓋
在上方的繼承中,子類沒有過載任何父類的函式,那麼在虛擬函式列表中,就代表著
虛擬函式按照宣告的順序放入表中。
父類的虛擬函式,在子類的虛擬函式的前面。
一般繼承,有虛擬函式覆蓋
此時記憶體中地址如下
注意,最重要的一點是,地址上,覆蓋的f()函式,被放置到了父類的f()函式上。於是就可以有下方的程式
Base *b = new Derive(); b->f();
使用一個型別為Base的指標b指向一個新建的例項Derive(),此時對於指標b指向的虛擬函式表中,此時f()的地址被Devieive函式的地址被覆蓋, 形成如上的虛擬函式表。
此時訪問成功
多重繼承 無虛擬函式的覆蓋
假設有如上的內容
此時虛擬函式表
每個父類都有自己的虛表,子類的成員函式被放到第一個父類的表中。即第一個父類的表是按照宣告的順序來判斷。
多重繼承 有虛擬函式覆蓋
此時表如下
此時父類的被替換了。
這樣就實現了多重繼承,程式碼如下
Derive d;
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f();
b2->f();
b3->f();
此時程式碼如上。
純虛擬函式
virtual int area() = 0;
實現一個純虛擬函式,此時可以在派生類中更好的定義純虛擬函式。