標籤:

C++對象模型(1) 關於對象

本系列主要探討與內存關聯最密切的C++語言,也是一個在不斷的學習中的系列。

1.1 C++對象模式

C++對象的額外布局成本主要來源於虛函數引起的指向虛表的指針,以及虛繼承而來的父類。首先說一下虛表,每一個類產生出的一堆指向虛函數的指針,全部放在表格中,這個表格即是虛表,而每一個類對象中只要存在虛函數,就會多出一個指向自己類所屬的虛表的指針,虛表指針一般與機器有關,32位機一般大於4位元組,64位機一般大於8位元組,除了指針本身的大小之外一般會附帶一些額外信息,與機器以及編譯器有關。

上圖為C++對象的數據存儲方式,可以看到對象本身擁有的只是局部數據,以及虛表指針,類的靜態變數儲存在全局。

那麼虛繼承又是怎麼實現的呢,虛繼承是為了阻止菱形繼承而出現的一種繼承方式,首先虛繼承的基類會被視為一個模板,在編譯期間會為此模板生成一個唯一實現,每被虛繼承一次就會相應的產生一個指向此實現的指針,並存儲在bptr表內,此實現也會產生一個類似於虛表指針的指針,它指向存儲指針的bptr表。

1.2 關鍵詞帶來的差異

struct與class的差別並沒有多少,如果說有的話只有struct內成員默認為public,class內成員默認為private,但是設計者在設計之初並不想保留struct,只是為了與C兼容,因此C++相對於C的新的特性並不適用於struct,比如template,因此struct最好只用來作為數據集合,並且只傳輸數據集,而不用來做其它事情。

1.3 對象的差異

C++程序設計直接支持三種程序設計範式:程序模型(傳統的C),抽象數據模型(運算定義仍然隱而未明),面向對象模型(通過抽象基類被封裝)。

C++以三種方法支持多態:指針類型的隱式轉換,虛函數機制,dynamic_cast一類的顯示類型轉換。多態的作用是經由一個共同的介面(函數名稱)影響類型的封裝(忽略類的差異)。

那麼一個對象的大小該怎麼計算呢?包括非靜態成員的大小,加上對齊效果,加上支持虛函數的額外負擔。

1.4 指針布局

指針都是一個機器字長大小,內部存儲了一個地址,那麼指針類型影響的是什麼呢?影響的是定址出來的對象類型的不同,也就是說指針類型會告訴編譯器如何解釋這個地址。因此void*類型的變數應當無法通過指針本身進行操作。

在編譯期間,指針類型將決定可調用的固定介面,以及此介面的准許等級(public,protected,private),在多態情況下,類型信息的封裝存在於虛表,虛表指針所在的鏈表中,現在有如下情況:

Bear b;

ZooAnimal za=b;//引起切割,ZooAnimal是Bear的父類

za.rotate();//調用ZooAnimal::roteta()

為什麼rotate(),調用的是ZooAnimal?以子類初始化父類將會被切割,被切割之後的對象中將不存在與子類有關係的任何東西,因此不再呈現多態。

為什麼b的拷貝不會覆蓋虛表指針?因為編譯器會確保某個對象含有多個虛表指針時,虛表指針不會被基類對象初始化或改變。


推薦閱讀:

TAG:遊戲編程 |