先上面試題,問列印結果是什麼?
#include
class a
};class b : public a
};class c : public b
};class d : public c
virtual
void
fund
()};
class e : public d
virtual
void
fune
()};
class e1 : public d
virtual
void
fune1
()};
intmain
()
再看到這裡的時候,再做一遍吧,答案放在最後
問題:類中的記憶體布局是什麼樣的?
類的非靜態成員函式是放在什麼地方的?
虛函式是放在什麼地方的?虛表是在**,以及如何儲存的?
簡單的結構如下圖,參考[2]描述地超級詳細,也利於理解,可以仔細閱讀一下。
c++物件模型
另外,為了解題,還要講講編譯鏈結以及函式呼叫的簡單原理
編譯的時候,靜態以及非靜態成員函式也是已經編譯好了的。虛函式,編譯器能夠確定的是偏移量,待執行的時候會去找具體物件的vptr,指向該物件所在類對應的虛表,找到真正應該呼叫的函式[3]。 文章[4]也說到了,虛函式和普通函式的在彙編一層的表現形式,虛函式要通過虛表去查詢真正該呼叫的函式,而普通函式直接就找到了的。
現在,運用上面的結論來回答問題
看一下測試**和結果如下: 在上面面試題後面加入以下測試**
printf("**********=vritual address*****====\n");
printf("vtable p1: %x\n", *((int*)p1));
printf("vtable p2: %x\n", *((int*)p2));
printf("vtable p3: %x\n", *((int*)p3));
printf("vtable p4: %x\n", *((int*)p4));
printf("vtable p5: %x\n", *((int*)p5));
printf("vtable p6: %x\n", *((int*)p6));
printf("p1 virtual fun: %x\n", *(int*)*((int*)p1));
printf("p2 virtual fun: %x\n", *(int*)*((int*)p2));
printf("p3 virtual fun: %x\n", *(int*)*((int*)p3));
printf("p4 virtual fun: %x\n", *(int*)*((int*)p4));
printf("p5 virtual fun 1: %x, offset: %x\n", *(int*)*((int*)p5), &e::fun);
printf("p5 virtual fun 2: %x, offset: %x\n", *((int*)*((int*)p5)+1), &e::fund);
printf("p5 virtual fun 3: %x, offset: %x\n", *((int*)*((int*)p5)+2), &e::fune);
printf("p6 virtual fun 1: %x, offset: %x\n", *(int*)*((int*)p6), &e1::fun);
printf("p6 virtual fun 2: %x, offset: %x\n", *((int*)*((int*)p6)+1), &e1::fund);
printf("p6 virtual fun 3: %x, offset: %x\n", *((int*)*((int*)p6)+2), &e1::fune1);
結果:
**********=vritual address*****====
vtable p1: 8049290
vtable p2: 8049258
vtable p3: 80492a0
vtable p4: 80492a0
vtable p5: 80492c0
vtable p6: 80492c0
p1 virtual fun: 8048e1e
p2 virtual fun: 8048e0a
p3 virtual fun: 8048e32
p4 virtual fun: 8048e32
p5 virtual fun 1: 8048e46, offset: 1
p5 virtual fun 2: 8048dce, offset: 5
p5 virtual fun 3: 8048de2, offset: 9
p6 virtual fun 1: 8048e46, offset: 1
p6 virtual fun 2: 8048dce, offset: 5
p6 virtual fun 3: 8048de2, offset: 9
p1雖然在編譯的時候fun是類a的,但是物件畢竟還是b物件,所以vptr的位址指向的依舊是b類的虛表位址,可以再新增一條列印b物件的虛表的測試**,發現也是0x8049170;
p3和p4,p5和p6的虛表位址是一樣的,這也說明了他們是指向同乙個類的例項(物件)的;
p3和p4,p5和p6的第乙個,第二個,第三個的虛函式位址都一樣,也同樣說明了是同乙個類中的虛表;
也可以看到fund的偏移量是5(和1之間的差是4,測試機器是32位的),fune和fune1都是偏移9,所以能夠通過pe1呼叫e物件的第三個虛函式。
[1]圖說c++物件模型:物件記憶體布局詳解
[2]c++虛表,你搞懂了嗎?
[3]c/c++雜記:虛函式的實現的基本原理
[4]關於c++虛函式與普通函式的編譯與呼叫機制
答案:a, c, d, d, d_test, e_test, d_test
一道C面試題的思考
c語言真的是學無止境的感覺,大部分同學大學都會開設c語言課程。很多人把c語言二級過了就感覺入門了 對於那些在做嵌入式開發的工程師,幾乎每天都要接觸c語言,很多人會感覺自己c語言學得很溜了。那好,咱們用一道c語言面試題來測試一下。首先給出題目 定義乙個巨集,求兩個數中的最大數 ok,很多人應該能很快寫...
一道面試題的思考
在繼承中new和override相同點和區別?看下面的 有乙個基類a,b1和b2都繼承自a,並且使用不同的方式改變了父類方法print 的行為。測試 輸出什麼?為什麼?public void dotest public class a public class b1 a public class b...
一道面試題引發的思考
首先我們給出這道面試題的 以及題目 lista new arraylist list.add 1 list.add 2 for string item list 問 上段 執行會報錯嗎?如果把 1 換成 2 會報錯嗎?為什麼?首先給出答案 上面這段 執行不會報錯。把 1 換成 2 再執行就會報錯。為...