關於C 多型性的問題,一個關於C 多型性的問題

時間 2021-10-14 22:40:00

1樓:真水無香耶

多型性(polymorphism)是物件導向程式設計的基本特徵之一。而在c++中,多型性通過虛擬函式(virtual function)來實現。我們來看一段簡單的**:

#include

using namespace std;

class base

virtual void fun2()

virtual void fun3()

};class a:public base

void fun2()

};void foo (base& obj)

int main()

執行結果為:

base::fun1()

base::fun2()

base::fun3()

a::fun1()

a::fun2()

base::fun3()

僅通過基類的介面,程式呼叫了正確的函式,它就好像知道我們輸入的物件的型別一樣!

那麼,編譯器是如何知道正確**的位置的呢?其實,編譯器在編譯時並不知道要呼叫的函式體的正確位置,但它插入了一段能找到正確的函式體的**。這稱之為晚**(late binding)或執行時**(runtime binding)技術。

通過virtual關鍵字建立虛擬函式能引發晚**, 編譯器在幕後完成了實現晚**的必要機制。它對每個包含虛擬函式的類建立一個表(稱為vtable),用於放置虛擬函式的地址。在每個包含虛擬函式的類中,編譯器祕密地放置了一個稱之為vpointer(縮寫為vptr) 的指標,指向這個物件的vtable。

所以無論這個物件包含一個或是多少虛擬函式,編譯器都只放置一個vptr即可。vptr由編譯器在建構函式中祕密地插入的**來完成初始化,指向相應的vtable,這樣物件就「知道」自己是什麼型別了。vptr都在物件的相同位置,常常是物件的開頭。

這樣,編譯器可以容易地找到物件的vtable並獲取函式體的地址。

如果我們用sizeof檢視前面base類的長度,我們就會發現,它的長度不僅僅是一個int的長度,而是增加了剛好是一個void指標的長度(在我的機器裡面,一個int佔4個位元組,一個void指標佔4個位元組,這樣正好類base的長度為8個位元組)。

每當建立一個包含虛擬函式的類或從包含虛擬函式的類派生一個類時,編譯器就為這個類建立一個唯一的vtable。在vtable中,放置了這個類中或是它的基類中所有虛擬函式的地址,這些虛擬函式的順序都是一樣的,所以通過偏移量可以容易地找到所需的函式體的地址。假如在派生類中沒有對在基類中的某個虛擬函式進行重寫(overriding),那麼還使用基類的這個虛擬函式的地址(正如上面的程式結果所示)。

至今為止,一切順利。下面,我們的試驗開始了。

就目前得知的,我們可以試探著通過自己的**來呼叫虛擬函式,也就是說我們要找尋一下編譯器祕密地插入的那段能找到正確函式體的**的足跡。

如果我們有一個base指標作為介面,它一定指向一個base或由base派生的物件,或者是a,或者是其它什麼。這無關緊要,因為vptr的位置都一樣,一般都在物件的開頭。如果是這樣的話,那麼包含有虛擬函式的物件的指標,例如base指標,指向的位置恰恰是另一個指標——vptr。

vptr指向的 vtable其實就是一個函式指標的陣列,現在,vptr正指向它的第一個元素,那是一個函式指標。如果vptr向後偏移一個void指標長度的話,那麼它應該指向了vtable中的第二個函式指標了。

這看來就像是一個指標連成的鏈,我們得從當前指標獲取它指向的下一個指標,這樣我們才能「順藤摸瓜」。那麼,我來介紹一個函式:

void *getp (void* p)

我們不考慮它漂亮與否,我們只是試驗。getp() 可以從當前指標獲取它指向的下一個指標。如果我們能找到函式體的地址,用什麼來儲存它呢?我想應該用一個函式指標:

typedef void (*fun)();

它與base中的三個虛擬函式相似,為了簡單我們不要任何輸入和返回,我們只要知道它實際上被執行了即可。

然後,我們負責「摸瓜」的函式登場了:

fun getfun (base* obj, unsigned long off)

第一個引數是base指標,我們可以輸入base或是base派生物件的指標。第二個引數是vtable偏移量,偏移量如果是0那麼對應fun1(),如果是1對應fun2()。getfun() 返回的是fun型別函式指標,我們上面定義的那個。

可以看到,函式首先就對base指標呼叫了一次getp(),這樣得到了vptr這個指標,然後用一個 unsigned char指標運算偏移量,得到的結果再次輸入getp(),這次得到的就應該是正確的函式體的位置了。

那麼它到底能不能正確工作呢?我們修改main() 來測試一下:

int main()

激動人心的時刻到來了,讓我們執行它!

執行結果為:

a::fun1()

a::fun2()

base::fun3()

至此,我們真的成功了。通過我們的方法,我們獲取了物件的vptr,在它的體外執行了它的虛擬函式。

2樓:程式小兵

多型很多種狀態,

樓上已經把網上的資料複製過來了

仔細看看吧

關於C 的問題,關於C 的一個問題

d centigrade 5 9 d fahrenheit 32 這一句的9 d fahrenheit 32 是希望表示9 d fahrenheit 32 嗎?d centigrade 5 9 d fahrenheit 32 這一句,結合cannot be used as a function,編譯...

關於C語言的問題,求教,一個關於C語言的問題,求教!!

冰雨vs冰霜 a b 是賦值 a b 是等號 這是新手長犯錯誤 c語言中經常犯的毛病!初學者必看!c 語言的最大特點是 功能強 使用方便靈活。c編譯的程式對語法檢查並不象其它高階語言那麼嚴格,這就給程式設計人員留下 靈活的餘地 但還是由於這個靈活給程式的除錯帶來了許多不便,尤其對初學c語言的人來說,...

C 疑問,實現多型性一定要用到虛擬函式嗎

我的理解 多型性,即一個父類的引用,可以指向派生類的物件 引用的訪問許可權取決於引用的型別,而非物件型別 你這樣在派生類中,寫一個與父類具有相同簽名的成員方法,是覆蓋 有些地方說,只有虛方法才能覆蓋,你可以把派生類的方法註釋了,執行下,看看結果 這個和多型性貌似沒什麼關係.base p new de...