1.可以從現有的類派生出新類。這稱為類的繼承。新類稱為次類、子類或派生類。現有的類稱為超類、父類或基類。 2.構造方法用來構造類的實例。不同於屬性和方法,子類不繼承父類的構造方法。它們只能用關鍵字super從子類的構造方法中調用。 3.構造方法可以調用重載的構造方法或它的父類的構造方法。這種調用必須 ...
- 類的記憶體結構包含什麼?
靜態成員變數和靜態成員函數是不會計算在類的記憶體結構中的,因為靜態static決定了它們早在編譯期就確定了靜態變數區中的地址,因此通常來說類的記憶體結構只包含普通成員變數。 - 還有什麼特殊情況下不止包含普通成員變數呢?
- 繼承:當子類繼承父類時,父類的普通成員變數同樣會存儲在子類的記憶體結構之中
- 虛函數:子類繼承父類時,如果父類存在虛函數(父類存在虛函數,作為繼承類的子類必定也存在虛函數),那麼此時父類中存在一個虛函數指針,指向存儲虛函數的地址,虛函數指針位於類記憶體結構的頂部。
- 當virtual遇上繼承:虛繼承的存在,是用於面對菱形繼承的問題,在這裡假設將TrueBase稱為一級父類,Base1、Base2稱為二級父類,Son稱為三級子類。如果Base1、Base2同時作為TrueBase類的子類,而son類繼承了Base1和Base2類,比如動物,羊和駝,羊駝(切勿當真)。此時為了避免Son類中重覆存在TrueBase類的成員變數,將Base1和Base2定義為TrueBase的虛擬繼承子類(就像繼承了但沒有完全繼承)。
- 虛繼承是什麼原理呢?
虛繼承實際上是通過虛基類指針來尋找TrueBase類的地址,和虛函數指針一樣,是通過指針來指向記憶體地址,而不是像普通繼承那樣直接存入地址,形成空間浪費。 - 加入了繼承、虛函數、虛繼承之後,記憶體結構究竟變得怎麼樣了呢?
(Base1: virtual public TrueBase; Base2: virtual Public TrueBase; Son: public Base1, public Base2)
vfptr即虛函數指針,vbptr是虛基類指針,vfptr在記憶體中處於vbptr之前,且子類通過vbptr尋找的基類存儲在最後。 - 肯定有人問為什麼當發生虛繼承時,Base1和Base2的vfptr呢?Son的vbptr呢?
- 在macOS的GCC編譯器Clion平臺下,準確來說,是64位系統下的GCC編譯器中,子類父類會共用一個虛函數指針!!!,所以虛函數指針只有一個!
- csdn中的推薦鏈接,給了我很大的啟發和幫助,感謝作者:https://blog.csdn.net/longjialin93528/article/details/79874558
- 問vbptr的去好好面壁思過(笑),只有虛擬繼承才會出現虛基類指針,此時Son只有public繼承,是不會存在虛基部指針的。當然,如果是虛繼承,那麼自然會多出一個vbptr指針。
- 記憶體對齊的問題
需要註意的是,不同編譯器下的不同數據類型的大小是不同的,在64位系統下,我的macOS,Clion,GCC編譯器下指針是8位元組。為了使記憶體最大程度地被利用而且規範,會存在記憶體對齊的要求。
以最長的虛繼承的Son類型為例,大小應該是vbptr(8)+int(8)+vbptr(8)+int(4)+int(4)+vfptr(8)+int(8)= 48。因為存在記憶體對齊,int這種4大小的類型,如果不能組合為8大小,將會由4擴至8,在Clion中編寫,大小確實是48,證明推斷並無錯誤。 - 總結
儘管類的記憶體結構很複雜,但是我們只要掌握這些最基本的結構知識,還是能夠推斷出大部分情況下的記憶體大小的。
但是,不要為了所謂的學習,去故意寫一些亂七八糟的繼承,不僅從實際出發不實用,而且會讓自己陷入陷阱中,在日常學習和編程中加以利用這些知識才是正途。