2012-03-30 50 views
1

我試圖更好地理解虛擬繼承在實際中如何工作(即,不是按照標準,而是在像g++這樣的實際實現中)。真正的問題在底部,用粗體表達。虛擬繼承表如何在g ++中工作?

所以,我給自己建一個繼承圖,其中有除其他事項外,這些簡單類型:

struct A { 
    unsigned a; 
    unsigned long long u; 
    A() : a(0xAAAAAAAA), u(0x1111111111111111ull) {} 
    virtual ~A() {} 
}; 

struct B : virtual A { 
    unsigned b; 
    B() : b(0xBBBBBBBB) { 
    a = 0xABABABAB; 
    } 
}; 

(在整個層次我也有一個C: virtual ABC: B,C,使虛擬繼承纔有意義)

我編寫了幾個函數來轉儲實例的佈局,採用vtable指針並打印前6個8字節值(任意適合屏幕),然後轉儲對象的實際內存。這看起來類似的東西:

傾倒一個A對象:

actual A object of size 24 at location 0x936010 
vtable expected at 0x402310 { 
      401036,   401068,   434232,    0,    0,    0, 
} 
1023400000000000aaaaaaaa000000001111111111111111 
[--vtable ptr--] 

傾倒B對象,並且其中A對象所在,這是由在各自的位置大量印A S表示。

actual B object of size 40 at location 0x936030 
vtable expected at 0x4022b8 { 
      4012d2,   40133c,  fffffff0,  fffffff0,   4023c0,   4012c8, 
} 
b822400000000000bbbbbbbb00000000e022400000000000abababab000000001111111111111111 
           AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (offset: 16) 

正如你所看到的,BA部位於一個的16字節偏移到B對象(可以是不同的起點,如果我實例化BC它DYN-澆鑄爲B*)。

我本來期望的16(或至少是2,由於對齊),以某處顯示在表,因爲該方案具有在運行時查找的A實際位置(偏移)。 那麼,佈局怎麼樣?


編輯:轉儲是通過調用dumpdumpPositions完成:

using std::cout; 
using std::endl; 

template <typename FROM, typename TO, typename STR> void dumpPositions(FROM const* x, STR name) { 
    uintptr_t const offset {reinterpret_cast<uintptr_t>(dynamic_cast<TO const*>(x)) - reinterpret_cast<uintptr_t>(x)}; 
    for (unsigned i = 0; i < sizeof(FROM); i++) { 
    if (offset <= i && i < offset+sizeof(TO)) 
     cout << name << name; 
    else 
     cout << " "; 
    } 
    cout << " (offset: " << offset << ")"; 
    cout << endl; 
} 
template <typename T> void hexDump(T const* x, size_t const length, bool const comma = false) { 
    for (unsigned i = 0; i < length; i++) { 
    T const& value {static_cast<T const&>(x[i])}; 
    cout.width(sizeof(T)*2); 
    if (sizeof(T) > 1) 
     cout.fill(' '); 
    else 
     cout.fill('0'); 
    cout << std::hex << std::right << (unsigned)value << std::dec; 
    if (comma) 
     cout << ","; 
    } 
    cout << endl; 
} 
template <typename FROM, typename STR> void dump(FROM const* x, STR name) { 
    cout << name << " object of size " << sizeof(FROM) << " at location " << x << endl; 
    uintptr_t const* const* const vtable {reinterpret_cast<uintptr_t const* const*>(x)}; 
    cout << "vtable expected at " << reinterpret_cast<void const*>(*vtable) << " {" << endl; 
    hexDump(*vtable,6,true); 
    cout << "}" << endl; 
    hexDump(reinterpret_cast<unsigned char const*>(x),sizeof(FROM)); 
} 
+0

這將是很好/有助於看到'轉儲'的代碼。 – gbulmer 2012-03-30 17:40:58

+0

@gbulmer:你走了。雖然我不認爲它有多大的幫助。 – bitmask 2012-03-30 17:45:05

回答

1

答案實際上是記錄在這裏,在Itanium ABI。特別是第2.5節包含虛擬表格的佈局。

+0

這是我的第二個猜測,但正如你可以在轉儲中看到的那樣,'B'只有16個字節(除了'A'部分)。前8個字節是vptr,而後8個字節是'b'整數成員(對齊)。至少,我是這麼讀的。我期望偏移16 **或**地址'0x4022c8'('== 0x4022b8 + 16')某處。不過,我現在要看看你的鏈接。 – bitmask 2012-03-30 17:50:34

+0

虛擬表格描述(類別3)有很多注意事項。你知道這個的語法描述嗎? – bitmask 2012-03-30 18:03:49