如下面的base類定義:
其物件在記憶體中的分配如下:
在此模型中,non static資料成員被放置到物件內部,static資料成員(放置到全域性資料區),static函式成員(放置到全域性資料區),non static函式成員(放置到**段)均被放到物件之外。對於虛函式的支援則分兩步完成:
每乙個class產生一堆指向虛函式的指標,放在**中。這個**稱為虛函式表(virtual table,vtbl)。
每乙個物件被新增乙個指標,指向相關的虛函式表vtbl。稱這個指標為vptr。vptr的設定和重置都由每乙個class的建構函式、析構函式和拷貝賦值運算子自動完成。
另外,虛函式表的位址前面設定了乙個指向type_info的指標,rtti執行時型別識別,由編譯器在編譯時生成特殊型別的資訊,包括物件繼承關係,物件本身描述,rtti是為多型而生成的資訊,所以只有具有虛函式的物件才會生成。
這個模型的優點在於它的空間和訪問時間的效率;缺點是如果應用程式本身未改變,但當其非靜態的成員函式新增、刪除或修改時,需要重新編譯。
無重寫,即派生類中沒有與基類同名的虛函式。有如下派生類:
base、derived的類圖如下所示:
base的模型跟上面一樣,不受繼承的影響。derived不是虛繼承,所以是擴充已存在的虛函式表,所以結構如下所示:
派生類中重寫了基類的print()函式。
base、derived_overwrite的類圖如下所示:
重寫print()函式在虛函式表中如下:
從單繼承可以知道,派生類中只是擴充了基類的虛函式表。如果是多繼承的話,又是如何擴充的呢?
每個基類都有自己的虛表;
子類中的成員函式被放到了第乙個基類的表中;
記憶體布局中,其父類布局依次按宣告順序排列;
每個基類的虛表中的print()函式都被overwrite成了子類的print()。這樣做就是為了解決不同的基類型別的指標指向同乙個子類的例項,而能夠呼叫到實際的函式。
上面的三個類中,
derived_mutlip_inherit
繼承自base、base_1
兩個類,
derived_mutlip_inherit
的結構如下所示:
虛繼承是為了解決重複繼承中多個間接父類造成的二義性問題,所以不能簡單的擴充每個基類的虛函式表,這樣會造成基類有多個虛函式表。
虛繼承的派生類的記憶體結構,和普通繼承完全不同。虛繼承的子類,有單獨的虛函式表,另外也單獨儲存乙份父類的虛函式表,兩部分用0x00000000來做分界。派生類的記憶體中,首先是自己的虛函式表,然後是自己的資料成員,然後是0x00000000,然後就是基類的虛函式表和基類的資料成員。
如果派生類沒有自己的虛函式,就不會有自己的虛函式表,但是派生類的資料成員和基類之間,仍然需要0x00000000來間隔。
因此,在虛繼承中,派生類和基類的資料,是完全間隔的,先存放派生類自己的虛函式表和資料,中間以0x00000000間隔,最後儲存基類的虛函式表和資料。如果派生類複寫了基類的虛函式,則將派生類記憶體中基類的虛函式表的相應函式替換。
簡單虛繼承的兩個類base、
derived_virtual_inherit1的關係如下所示:
derived_virtual_inherit1
的物件模型如下圖:
菱形繼承關係如下圖:
derived_virtual
的物件模型如下圖:
【原創位址】:
c 物件模型
很久之前就想總結一下c 的記憶體使用機制。直到現在剛考完試之制,去實習之前,才有時間完成這事。1.程式使用記憶體區 乙個程式占用的記憶體區一般分為5種 1 全域性 靜態資料區 儲存全域性變數及靜態變數 包括全域性靜態變數和區域性靜態變數 2 常量資料區 儲存程式中的常量字串等。3 區 儲存程式的 4...
C 物件模型
很久之前就想總結一下c 的記憶體使用機制。直到現在剛考完試之制,去實習之前,才有時間完成這事。1.程式使用記憶體區 乙個程式占用的記憶體區一般分為5種 1 全域性 靜態資料區 儲存全域性變數及靜態變數 包括全域性靜態變數和區域性靜態變數 2 常量資料區 儲存程式中的常量字串等。3 區 儲存程式的 4...
C 物件模型
簡單物件模型 乙個c 物件儲存了所有指向成員的指標,而成員本身不儲存在物件中。也就是說不論資料成員還是成員函式,也不論這個是普通成員函式還是虛函式,它們都儲存在物件本身之外,同時物件儲存指向它們的指標。示意圖如右。簡單物件模型對於編譯器來說雖然極盡簡單,但同時付出的代價是空間和執行期的效率.顯而易見...