C++對象內存佈局測試總結

轉自:http://www.cnblogs.com/cswuyg/archive/2010/08/20/1804113.html

詳細看了一下虛繼承,發現說的最優道理的就是這個了。

另外有個小小特例,class A{ }; sizeof(A)=? 答案是1!^_^

外加一篇:http://hi.baidu.com/absolute8511/blog/item/2846613d0fb29ae73d6d978f.html

 

--------------------------------我是轉載分割線------------------------------------

 

C++對象內存佈局測試總結

  http://hi.baidu.com/%D6%F2%C7%EF/blog/item/826d38ff13c32e3a5d6008e8.html

  上文是半年前對虛函數、虛擬繼承的理解。可能有一些錯漏。而且只是理解了比較簡單的部分,表達也不夠清晰,這次決定花的時間再做一次總結。

  對於普通的C++對象內存佈局,簡單得不得了,就不做總結了。這裏只總結涉及到虛擬繼承的情況。

    因爲不同編譯器對虛擬繼承的實現採用不同的方式,所以要完整的分析是不可能的。這裏只考慮VS2005/2008,還有簡單涉及GCC編譯器。

1、 單個虛擬繼承

只是爲了分析而已,實際中並沒有太大的作用。跟虛擬繼承相關的派生類對象的內存佈局跟具體的編譯器相關。

   (1)VS編譯器:無論有無虛函數,必然含有虛基類表指針。虛基類表中的內容爲本類實例的偏移和基類實例的相對偏移值。如果有虛函數,那麼基類的虛函數表跟派生類的虛函數表是分開的。

在內存佈局上,地址從低到高,順序如下:派生類的虛函數表指針+虛基類表指針+派生類的成員變量+“間隔”(4個字節)+基類的虛函數表指針+基類的成員變量。派生類跟基類實例的位置關係跟普通繼承正好相反。

    說明:“間隔”產生的原因是派生類重寫了基類的虛函數。如果沒重寫,則這一項沒有。

    

圖 1 VS編譯器—單個虛擬繼承

   (2)GNU的GCC編譯器:跟VS的編譯器類似,有不同的地方是,虛基類表跟派生類的虛函數表合併。另外通過虛基類表指針往正負兩個方向尋址,可以獲得不同偏移值,也就是說有兩個功能一樣的虛函數表。不過在實際應用的時候,不知道虛基類表是否真的有用,測試了簡單的情況發現編譯器做了優化,根本就沒有用虛基類表來尋址虛基類實例。

 

圖 2 GCC編譯器—單個虛擬繼承

2、 虛擬繼承多個基類

虛基類表要增加內容,有N個虛基類就有N項基類實例偏移值,再加上1項本類實例的偏移值,也就是N+1。

假設C虛擬繼承了A類和B類,考慮最複雜的情況(都有虛函數),那麼C類對象的內存佈局如下

 

(VS編譯器):

C類虛函數表指針+虛基類表指針+C類成員變量+A類間隔(4個字節) + A類虛函數表指針+ A類成員變量+ B類間隔(4個字節)+B類虛函數表指針+ B類成員變量。

說明:當派生類重寫了該基類的虛函數,纔會有“間隔”。“間隔”屬於虛函數被重新實現了的虛基類,可能是一個標誌,也有可能是在函數調用的時候用上。不是很清楚。

 

圖 3 VS編譯器—虛擬繼承多個基類

(GCC編譯器):

C類虛函數表指針(包含虛基類表) + C類成員變量 + A類虛函數表指針 +  A類成員變量 + B類虛函數表指針 + B類成員變量。

相比較執行,使用GCC編譯器,派生類對象小一些。(圖略)

3、 虛擬繼承之菱形繼承

這裏的菱形繼承指的是:B、C虛擬繼承A,然後D普通繼承B、C。

D類的對象的內存佈局如下

(VS編譯器)

B類虛函數表指針(該虛函數表包含D類獨有的虛函數的地址)+B類虛基類表指針+B類成員變量+C類虛函數表指針+C類虛基類表指針+C類成員變量+D類成員變量+“間隔”+A類虛函數表指針+A類成員變量。

說明:如果A類的虛函數沒有被重寫,那麼就沒有“間隔”。

 

圖 4 VS編譯器—菱形繼承

(GCC編譯器)

把B、C類的虛函數表跟虛基類表合併就是了。(圖略)

4、VS編譯器,“間隔”的疑問

 “間隔”的問題,在沒有虛函數的情況下,重寫是沒有“間隔”的,所以覺得可能跟虛函數有關,也就是說是爲了實現多態,具體是用在哪個地方,做了簡單的反彙編調試(父類指針指向子類對象,調用被子類重寫了的虛函數),並沒有發現哪裏用到了“間隔”,可能要在複雜的調用纔會用上吧,目前搞不清楚。

5、虛基類表的問題

通過反彙編調試發現在使用多態的時候,VS編譯器會去使用虛基類表,用於尋址虛基類地址。而GCC編譯器則沒有這麼做,測試了比較簡單的情況,發現它做了優化,並沒有利用虛基類表,而是直接在派生類對象地址上加上一個常數,獲得虛基類實例的地址。

 

 

學習參考:

http://blog.csdn.net/haoel/archive/2008/10/15/3081328.aspx

http://blog.csdn.net/BlueDog/archive/2009/10/22/4711169.aspx

《深度探索C++對象模型》應該是比較好的資料,但我是想通過編譯器來理解它們的內存佈局,所以上邊的資料更好一些。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章