按慣例以例項做分析。
#include
using namespace std;
enum note;
class instrument
int main()
以上**出自 thinking in c++一書,所以我只是將對書內內容做總結跟位元組的理解描述出來,如有不解之處請參考書本。
我們知道,函式tune()(通過引用)接受乙個instrument,但也不拒絕任何從instrument派生的類。在main()中,無需型別轉換,就能將wind物件轉給tune(),這符合了向上型別轉換(upcasting):去乙個物件的位址(指標或引用),並將其作為基類的位址來處理。那麼問題就產生了,我們發現呼叫輸出的是instrument::play。這不是期望的輸出,因為我們的物件為wind,自然是希望呼叫wind::play。
解決這個問題需要理解**(binding),關於多型性,我們也常提到了動態**(dynamic binding),其機制是確定執行時物件的型別並呼叫合適的成員函式。
c++中,可以通過virtual關鍵字來宣告函式為虛函式,動態**只對virtual函式其作用,只在使用含有virtual函式的基類的位址時發生。
僅僅只需在宣告時使用關鍵字virtual,定義時並不需要,而如果乙個函式在基類中被宣告為virtual,那麼在所有的派生類中它都是virtual的。在派生類中virtual函式的重定義通常稱為重寫(overriding)。儘管在派生類的相應函式宣告前加virtual並無害,但顯累贅。
修改instrument2.cpp
//late binding with the virtual keyword
#include
using namespace std;
enum note;
class instrument
int main()
此時,只需在基類instrument的play函式前加上virtual便實現了動態繫結,此時根據傳入的wind物件而呼叫wind::play。
那麼該機制是怎樣實現的呢?
每當建立乙個包含有虛函式的類或從包含有虛函式的類派生乙個類時,編譯器就為這個類建立乙個惟一的虛表(vtable)。在這個表中,編譯器放置了在這個類中或在它的基類中所有已宣告為virtual的函式的位址。如果在這個派生類中沒有對在基類中宣告為virtual的函式進行重新定義,編譯器就使用基類的這個虛函式位址,而即不為虛也不被重寫的函式就以基類的函式位址加入vtable。
下面通過sizeof來說明vptr的存在。
//object sizes with/without virtual functions
#include
using
namespace
std;
class novirtual
int i() const
};class onevirtual
int i() const
};class twovirtual
virtual
int i() const
};int main()
{ cout
<
<
<
<
<
<
<
<
<
<
int :4
novirtual:4
void*:4
onevirtual:8
twovirtual:4
由此反映出,如果有乙個或多個虛函式,編譯器都只在這個結構中摻入乙個單個指標(vptr)。
那麼如何定位虛表呢?編譯器另外還為每個類的物件提供了乙個虛表指標(即vptr),這個指標指向了物件所屬類的虛表。
在程式執行時,根據物件的型別去初始化vptr,從而讓vptr正確的指向所屬類的虛表,從而在呼叫虛函式時,就能夠找到正確的函式。
那麼虛表指標在什麼時候,或者說在什麼地方初始化呢?
答案是在建構函式中進行虛表的建立和虛表指標的初始化:
在構造子類物件時,要先呼叫父類的建構函式,此時編譯器只「看到了」父類,並不知道後面是否後還有繼承者,它初始化父類物件的虛表指標,該虛表指標指向父類的虛表。當執行子類的建構函式時,子類物件的虛表指標被初始化,指向自身的虛表。
要注意:對於虛函式呼叫來說,每乙個物件內部都有乙個虛表指標,該虛表指標被初始化為本類的虛表。所以在程式中,不管你的物件型別如何轉換,但該物件內部的虛表指標是固定的,所以呢,才能實現動態的物件函式呼叫。
虛函式功能圖示:
編譯器從這個instrument指標開始,這個指標指向這個物件的起始位址。對於所有的instrument物件或由instrument派生的物件,它們的vptr都在物件的相同位置(通常在開頭)。vptr指向vtable的起始位址。所有的vtable都具有相同的順序,即不論我們在派生類中以什麼次序重寫都以基類中虛函式的次序為準。
而我們獲取到實際函式位址是在執行時,通過vptr+offset(vptr指向vtable起始位址,即加上偏移來呼叫相應函式)實現了晚**。
每個函式指標是兩個位元組長,若函式位址在第二個位置時,即需vptr+4.
C 虛函式機制實現多型性
2019.cpp 定義控制台應用程式的入口點。include stdafx.h include 定義抽象基類 包含有純虛函式的類就成為抽象類或者抽象基類 class shape virtual float volume const 定義該函式為純虛函式 virtual void shapename ...
多型性 虛函式
先來講講賦值相容規則。前面說過,派生類如果是從基類公有繼承的,則它會包含基類中除建構函式和析構函式外的所有成員,基類的公有成員也成為派生類的公有成員,又因為物件只能訪問類的公有成員,所以基類物件具有的功能,派生類物件都有。這樣就引出了賦值相容規則。賦值相容規則就是指在基類物件可以使用的地方都可以用公...
多型性 虛函式
虛函式是過載的另一種表現形式,是一種動態的過載方式。虛函式呼叫與函式體之間的聯絡在執行時才建立。c 中可以用基類的物件指標可以指向它的公有派生物件,當它指向公有派生類物件時,只能訪問派生類中從基類繼承來的成員,而不能訪問派生類中定義的成員。當指標指向不同的物件時,分別呼叫不同類的成員函式,如果將函式...