程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 多重虛繼承下的對象內存布局

多重虛繼承下的對象內存布局

編輯:C++入門知識

多重虛繼承下的對象內存布局


《深入C++對象模型》絕對是一本值得深讀的一本書,書裡多次出現一句話,“一切常規遇見虛繼承,都將失效”。這是一個有趣的問題,因為C++標准容忍對象布局的實現有較大的自由,出現了各編譯器廠商實現的方式不同。   今天談談visual studio2013多重虛繼承下對象布局。有錯不要客氣,不要吝啬你的留言,請直接開噴。       class y和class z都是從class x虛繼承來的子類(也叫派生類),class A是class y和class z的多重繼承子類。為了簡化問題,下面的data member都是none_static data member。不提static data member是為了描述起來簡單~    
#include<iostream>
class x
{
public:
    int _x;
};
class y : public virtual x
{
public:
    int _y;
};
class z : public virtual x
{
public:
    int _z;
};
class A:public z,public y
{
public:
    int _a;
};
    int main()
{
    std::cout <<"sizeof(x): "<< sizeof(x) << std::endl;
    std::cout <<"sizeof(y): "<< sizeof(y) << std::endl;
    std::cout <<"sizeof(A): "<< sizeof(A) << std::endl;
    std::cout <<"&A::_x :"<< &A::_x << std::endl;;
    std::cout << "&A::_y :" << &A::_y << " " << std::endl;
    std::cout << "&A::_z :" << &A::_z << " " << std::endl;
    std::cout <<"&A::_a :"<< &A::_a << " " << std::endl;

    getchar();
    return 0;
}

 

    輸出:   sizeof(x): 4 sizeof(y): 12 sizeof(A): 24   sizeof(x)和sizeof(y)的結果在我們的意料中:x中沒有虛函數,所以sizeof(x) = sizeof(int)。class y虛繼承自class x,它需要一個指針指向類似於virtual base table的指針(下面簡稱vbptr),指向自己實際的x對象地址,所以sizeof(y)=2*sizeof(int) + sizeof(vbptr),我使用32位編譯器生成的代碼,所以sizeof(y)=12.   按照我開始的料想,sizeof(A)應該等於sizeof(int)*4(分別是:_x,_y,_z,_a) + sizeof(vbptr)=20。這裡的vbptr指向了唯一的x實例。   (注意啦,我要開始了)   C++標准中有規定,子類的對象模型中,應該保持父類對象的布局。在上面,我們說了sizeof(y)=2*sizeof(int) + sizeof(vbptr),bingo~,這裡的sizeof(A)就是sizeof(y) + sizeof(z)=24   而vbptr因為出在了兩個父類中,已經不需要額外的一個vbptr指向了。   你是不是要問這個vbptr(虛基類表指針)到底是干什麼的?   我們知道虛繼承的父類x,無論它被派生多次,它在最後多重繼承的子類中,只存在一份實例。這樣可能出現這樣的情況:   y object_y;   A object_A;   object_A._x =1;   object_y = object_A;//這裡發生類對象的剪切,將子類中的非父類成員剪去,剩下的賦給父類。   可能你還沒有明白,沒關系,我再啰嗦一句~   抱歉沒有很好的繪圖工具,我們使用“|“來隔開內存中的成員。    object_y 的內存布局應該是這樣的:vbptr | _x | _y,注意先實例化父類,再實例化子類。   而,object_A的內存布局應該是這樣的:vbptr_z | _z | vbptr_y | _y | _a | _x,注意我們多重繼承的先後順序:class A:public z,public y,這個順序決定了z的對象布局在y對象前面。因為虛繼承的原因,我們對象布局發生了改變:先實例化不共享部分(也就是class y和class z中除了class x的成員),這個部分依然是先父類再子類。最後才是虛繼承的公共部分:class x的成員。   我們發現,直接進行內存布局的剪切行不通(紅色部分),這需要編譯器介入,但編譯器也不是神仙啊,它需要信息去找_x在哪裡,這就是vbptr中存的信息——它告訴編譯器去哪裡找_x,這裡的信息可能是偏移,也可能是指針。為什麼是表呢?而不是直接指向成員的指針呢?因為,如果你再虛繼承幾個父類,你的這個指針會越來越多,對象的規模也就越來越大,這不能容忍。使用表可以始終只占一個指針大小,只是需要間址查詢,但是這完全可以由編譯器優化之。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved