一些常見的筆試題(三)

先來看一段簡單的代碼。

 

 

這道題也是經常在筆試中會遇到的,很多人可能都不能完全答對,一般只要c++有一定的經驗,大部分都能答對前面兩個函數調用,對於剩下兩個可能就不太確定了,雖然感覺不太實用,但這的的確確能體現一個程序員對類繼承的理解深度。

有些人的可能答案是這樣的:

CDerived::func1

CBase::func2

CBase::func1

CBase::func2

但結果真是這樣的嗎?

首先我們分析一下上面的結果。

① 函數調用pBase->func1()的結果顯然是CDerived::func1。會c++的都知道,這是多態的表現,基類的指針指向派生類對象,如果該函數是虛函數,那會調用會調用當前實際對象的vtable表中對應的函數,也就是CDerived::func1。

② 函數調用pBase->func2()的結果是CBase::func2。原因在於:如果被調用的函數不是虛函數,那麼函數調用會根據指針的類型來調用,而這裏是基類指針在調用,所以最終調用的是基類的函數func2() ,輸出爲CBase::func2。

 

對於這句話,CDerived* pDerived = (CDerived*)&base,我原先的想法是,這肯定有問題,怎麼能直接將基類對象的指針強轉成派生類的指針呢,這樣肯定會引起對象的切割。但實際上,這和對象切割一點關係都沒有,如果是(CDerived)base,將對象base強轉成CDerived的對象,這才叫對象切割。所以,這裏直接進行指針轉化是沒有問題的。不就將指針的類型轉換一下嘛,又沒有改變指針本身的值,所以這裏沒有危險。

③ 對於函數調用pDerived->func1()來說,因爲之前進行了指針的轉換,將基類的對象base的地址賦值給派生類的指針,所以很多人就不確定了,到底是調用哪個類的函數呢,是CBase::func1還是CDerived::func1,結果往往是連懵帶猜,隨便寫個算了。其實如果再對vtable稍微深想一下的話,可能就知道應該是什麼樣的答案了。

我的想法是,無論是pBase->func1()還是pDerived->func1(),它們的做法都是需要先判斷函數fun1是不是虛函數,如果是的話,那會就會去vtable裏面去找,當前對象應該調用的那個函數。每個類在聲明一個或多個對象之後,如果類中有虛函數,那麼就會爲每個對象鑲嵌一個__vfptr的指針,用它來指向vtable表,並且同一個類的所有對象的__vfptr值和vtable表中的內容都是完全一樣的【但是,我有個疑問,因爲同一個類的所有對象共享一個__vfptr指針,到底__vfptr能不能看成類的靜態成員?這個估計與編譯器實現有關】。所以,對於pDerived->func1()來說,它會去pDerived所指向對象的的vtable表中查找一下fun1這個函數,而pDerived所指向對象是base,也就是基類的對象,因此,找到的函數就是CBase::func1,所以,答案是:CBase::func1。

④ 如果能理解②和③的話,對於函數調用pDerived->func2()來說,答案就已經很明確了,就是CDerived::func2了。因爲fun2不是虛函數,所以函數調用的時候不會去vtable中去找,也就是說,fun2的調用不能根據對象的實際類型來找對應的函數,而只能根據調用的指針的類型來判斷調用的函數。

所以,最終的結果是:

CDerived::func1

CBase::func2

CBase::func1

CDerived::func2

 

因爲我對編譯原理不怎麼了解,沒學過,有些東西可能還是沒說明白甚至是說錯了,如有問題,謝謝糾正。

 

發佈了60 篇原創文章 · 獲贊 32 · 訪問量 44萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章