深度探索C 物件模型 Data語意學筆記

2021-05-23 04:43:11 字數 4354 閱讀 6150

class x ;

class y : public virtual x ;

class z : public virtual x ;

class a : public y, public z ;

它們的sizeof結果如下:

sizeof(x) = 1;

sizeof(y) = 8; //視編譯器不同而不同

sizeof(z) = 8; //視編譯器不同而不同

sizeof(a) = 12;

實際上,class x 並不是空,它有乙個晦澀的1bytes,那是被編譯器安插進去的乙個char, 這使得這個class的兩個物件將在記憶體中有不同的位址。

而class y 和 class z 與三個因素有關。

一:語言本身造成的額外負擔。當語言支援virtual base class 時,會導致一些額外的負擔。在派生類中,這個額外負擔反映在指標身上,它或者是指向virtual base class subobject;或者指向乙個**,**中存放的若不是virtual base class subobject的位址,就是其偏移量。

二:編譯器對於特殊情況所提供的優化處理。virtual base class x 的subobject的1bytes大小也出現在class y 和 z 身上。傳統上它被放在派生類固定部分的尾端。而某些編譯器會對empty virtual base class 提供特殊支援。

三:alignment 的限制。class y 和 z 目前為5位元組,為了進行對齊,將會被變成8位元組。

而在某些編譯器中,比如visual c++中,結果為1,4,4,8。這是因為編譯器提供了特殊處理。它的模型如下:

對於class a,它的大小是多少呢?由以下決定(乙個虛擬繼承的基類只有乙個實體,不管被繼承了多少份)

二:base y 的大小,為4位元組。z也一樣。

三:class a 自己的大小0位元組。

四:alignment的情況。這個時候為9位元組,為了對齊,所以為12位元組。

而visual c++進行特殊處理後,base y + base z 的大小為8位元組。

origin. chunksize = 250 等價於 point3d::chunksize = 250。

pt->chunksize = 250 等價於 point3d::chunksize = 250。

在c++中,這是「通過乙個指標和通過乙個物件來訪問資料成員時,結論完全相同」的唯一一種情況因為此時的member並不在物件中

point3d origin, *pt = &origin;

origin.x = 0.0

pt->x = 0.0

"從origin訪問「和」從pt訪問'有什麼重大的差異嗎? 答案是「當point3d是個派生類,而x資料成員是基類的資料成員時,就有重大的差異。對於指標訪問資料而言,這個操作必須延遲到執行時期進行訪問。而如果使用origin,則在編譯時期就確定了。

class concrete

private:

int val;

char c1;

char c2;

char c3;

sizeof(concrete) = sizeof(val) + sizeof(char)*3 + alignment = 8;

class concrete1

private:

int val;

char c1;

class concrete2:public concrete1

private:

char c2;

class concrete3: public concrete2

private:

char c3;

這個時候,sizeof(concrete3)並不等於8。

sizeof(concrete3) = sizeof(concrete1) + sizeof(concrete2) + sizeof(concrete3) = 8 + 4 + 4 = 16。

因為虛擬繼承只有乙個基類,如何對虛擬繼承的基類進行布局呢?一種方法是把派生類分成兩個部分:乙個不變區域性和乙個共享區域性。

一般的布局策略是先安排好derived class 的不變部分,然後再建立其共享部分。那麼如何訪問class的共享部分呢?cfront 編譯器會在每乙個派生類物件中安插一些指標,每個指標指向乙個virtual base class。這有兩個問題:

一:每乙個物件必須針對其每乙個虛基類揹負乙個額外的指標。

二:由於虛擬繼承串鏈的加長,導致間接訪問層次的增加。這裡的意思是:如果我有三層虛擬,我就需要三次間接訪問。

metware和其它編譯器對於第二個問題的解決方法是:複製巢狀的虛擬類的指標放到派生類物件中,雖然付出了一些空間上的代價,但是訪問時間不會隨著繼承的增加而增加時間。

至於第乙個問題,一般而言有兩個解決方法。microsoft編譯器引入所謂的virtual base class table。virtual base class指標放在這個**中。第二個解決方法是:是在virtual function table中放置virtual base class的offset。

class point3d

public:

virtual ~point3d();

static point3d origin;

float x, y, z;

那麼& point3d::z; 得到的是什麼? 將得到z 在類物件中的偏移量。如果vptr放在物件的起頭,則三個座標值在物件布局中的offset分別是4, 8 , 12。然而我們若去取data members的位址時, 傳回的值應該是多1的, 也就是1, 5, 9。因此:

printf("%p/n", &point3d::x);

printf("%p/n", &point3d::y);

printf("%p/n", &point3d::z);

在vc6.0中編譯得到的結果是:

0000000c

在bcb3中編譯得到的結果是:

0000000d

這說明vptr放在編譯器的起頭。vc6.0得到的結果可能進行了特殊處理。好!現在回到正題,為什麼值要加1呢?看下面這個例子。

float point3d::*p1 = 0;

float point3d::*p2 = &point3d::x;

if( p1 == p2 )

cout << "p1 & p2 contain the same value --";

cout << " they must address the same member!" << endl;

為了區分p1和p2, 每乙個真正的member offset值都被加上1。 因此,不論編譯器或使用者都必須記住,在真正使用該值以指出乙個member之前,請先減掉1。

為了測試vc6.0的值為什麼沒有加1,我測試程式如下:

class point3d

public:

static point3d origin;

float x, y, z;

int main()

float point3d::*p1 = 0;

float point3d::*p2 = &point3d::x;    

if( p1 == p2 )

cout << "p1 & p2 contain the same value --";

cout << " they must address the same member!" << endl;

return 0;

發現p1的值為0xffffffff, p2的值為0x00000000。不執行輸出。

if( p1 == null )

cout << "p1 & p2 contain the same value --";

cout << " they must address the same member!" << endl;    

p1的值為0xffffffff, 執行輸出。

但是null的值為0啊!為什麼會執行輸出呢?

我的猜測是vc6.0對指標的值還是加1了,列印出來的結果是減1後的。

因此:& point3d::z; 和 & origin.z之間的差異,就非常明確了。前者取」它在類中的偏移量,而後者取類物件中z的真正的位址「。

深度探索C 物件模型學習筆記 Data語意學

例子 class x class y public virtual x class z public virtual x class a public y,public z 物件大小由下述原因決定 1.語言本身所造成的額外負擔 如 支援virtual base classes時,derived cl...

深度探索C 物件模型之Data語意學讀書筆記

3.4繼承與data member 測試原始碼 class concrete1 class concrete2 public concrete1 class concrete3 public concrete2 對於此例子,使用vc 編譯,程式執行結果與書中討論相符,sizeof concrete3...

深度探索C 物件模型 語意學

有三種情況,會以乙個object的內容作為另乙個class object的初值 cpp class x x x x xx x 情況1,賦值物件 extern void foo x x void bar x foo bar default memberwise initalization 如果clas...