2008-12-28 35 views
12

對象大小以下代碼打印20,即的sizeof(z)是20.問題上多重繼承,虛基類,並在C++

#include <iostream.h> 
class Base 
{ 
     public: 
      int a; 
}; 

class X:virtual public Base 
{ 
     public: 
      int x; 
}; 

class Y:virtual public Base 
{ 
     public: 
      int y; 
}; 

class Z:public X,public Y 
{ 
}; 

int main() 
{ 
Z z; 
cout << sizeof(z) <<endl; 
} 

然而,如果我在這裏不使用虛擬基類,即用於下面的代碼: 的sizeof(z)是16.

#include <iostream.h> 
class Base 
{ 
     public: 
      int a; 
}; 

class X:public Base 
{ 
     public: 
      int x; 
}; 

class Y:public Base 
{ 
     public: 
      int y; 
}; 

class Z:public X,public Y 
{ 
}; 

int main() 
{ 
Z z; 
cout << sizeof(z) <<endl; 
} 

爲什麼是在第一種情況下的sizeof(z)的多個(20)? 難道不應該是12,因爲基地將被納入 只有Z軸一次?

回答

20

讓我們看看這兩種情況的客艙佈局。

沒有虛擬,你有兩個基類(「X」和「Y」)與每個的整數,並且每個這些類的已經集成到他們的「基礎」基類也具有一個整數。這是4個整數,每個32位,共計16個字節。

Offset Size Type Scope Name 
    0  4 int Base  a 
    4  4 int  X  x 
    8  4 int Base  a 
    12  4 int  Y  y 
    16 size (Z members would come at the end) 

(編輯:我已經寫在DJGPP程序獲得的佈局和調整表佔它)

現在讓我們談談虛擬基類:它們所取代的實際實例該類帶有指向共享實例的指針。你的「Z」類只有一個「Base」類,並且「X」和「Y」的實例都指向它。因此,在X,Y和Z中有整數,但只有一個Z.這意味着您有三個整數或12個字節。但X和Y也有一個指向共享Z的指針(否則他們不知道在哪裏找到它)。在32位機器上,兩個指針將增​​加額外的8個字節。這總共是你看到的20。內存佈局可能是這個樣子(我沒有驗證它...在ARM有一個示例,其中排序是X,Y,Z,然後基地):

Offset Size  Type Scope Name Value (sort of) 
    0  4 Base offset  X  ? 16 (or ptr to vtable) 
    4  4   int  X  x 
    8  4 Base offset  Y  ? 16 (or ptr to vtable) 
    12  4   int  Y  y 
    16  4   int Base  a 
    20 size (Z members would come before the Base) 

所以內存的區別是兩件事的組合:少一個整數和兩個指針。與另一個答案相反,我不相信vtables支付任何(編輯)直接(/編輯)卷,因爲沒有虛擬功能。

編輯:ppinsider提供了更多關於gcc案例的信息,其中他演示了gcc通過使用一個空白的虛擬表格(即沒有虛擬函數)來實現指向虛擬基類的指針。這樣,如果有虛函數,它不需要類實例中的額外指針,需要更多內存。我懷疑它的缺點是要進入基礎類的另一個間接途徑。

我們可能希望所有的編譯器要做到這一點,但也許不是。虛擬基類沒有提及vtables。第235頁特別說明了「具有虛擬函數的虛擬基類」,並且有一個圖表表示存儲器佈局的圖,其中存在與指向vtable的指針分開的X和Y部分的指針。我建議任何人不要理所當然地指望Base將會以表格的形式實現。

+0

馬克,你是怎麼產生這種佈局的? – 2008-12-28 17:57:04

+0

如果您指的是表格格式,那只是代碼。我只是猜測內容。我會更詳細地編輯我的答案,並參考ppinsider的答案。 – markets 2008-12-31 00:30:28

9

馬克Santesson的回答是相當多的錢,但assertation,有沒有虛函數表不正確。您可以使用g ++ -fdump-class-hierarchy來顯示正在發生的事情。這裏是沒有虛擬的情況:

Class Base 
    size=4 align=4 
    base size=4 base align=4 
Base (0x19a8400) 0 

Class X 
    size=8 align=4 
    base size=8 base align=4 
X (0x19a8440) 0 
    Base (0x19a8480) 0 

Class Y 
    size=8 align=4 
    base size=8 base align=4 
Y (0x19a84c0) 0 
    Base (0x19a8500) 0 

Class Z 
    size=16 align=4 
    base size=16 base align=4 
Z (0x19b1800) 0 
    X (0x19a8540) 0 
    Base (0x19a8580) 0 
    Y (0x19a85c0) 8 
    Base (0x19a8600) 8 

要特別注意「基本大小」的論點。現在,虛函數的情況下,並只顯示Z:

Class Z 
    size=20 align=4 
    base size=16 base align=4 
Z (0x19b3000) 0 
    vptridx=0u vptr=((& Z::_ZTV1Z) + 12u) 
    X (0x19a8840) 0 
     primary-for Z (0x19b3000) 
     subvttidx=4u 
    Base (0x19a8880) 16 virtual 
     vbaseoffset=-0x0000000000000000c 
    Y (0x19a88c0) 8 
     subvttidx=8u vptridx=12u vptr=((& Z::_ZTV1Z) + 24u) 
    Base (0x19a8880) alternative-path 

注意「基地規模」是相同的,但「大小」是一個指針多,並注意現在有一個虛函數表指針!這反過來又包含了父類的構造虛函數表,以及所有,級間魔法(建設虛函數表和虛擬表的表(VTT)),如下所述:

http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

注意,實際功能調度vtable將是空的。