2016-12-15 25 views
0

假設下面的代碼與基本結構基指針如何能夠知道派生類實例中基礎構件的內存位置?

struct A {int aMember}; 
struct B {bool bMember}; 
struct C {double cMember}; 
struct BA : B, A {}; 
struct CB : C, B {} ; 

void test(B *base) { 
    bool retrieved = base->bMember; 
} 

在這裏,測試功能能夠被傳遞的指針B,BA,或CB的一個實例。底層成員「bMember」的檢索是如何以低級方式實現的?據推測,該成員不能保證位於每個派生類型的傳遞對象收件人的給定偏移處。總之,對於任何給定派生類型,B的成員片段在哪裏「已知」如何?這是否在運行時通過某些與使用繼承的對象和類關聯的元數據來實現?

如果已經發布了一個簡單的解釋,我非常抱歉。我只是不知道如何將我的搜索短語返回給相關答案。

謝謝!

+0

https://isocpp.org/wiki/faq/classes-and-objects#layout-obj這可能有幫助。 –

+0

因爲它們是基本指針。他們指向基礎對象。派生對象中的內容完全不相關。 – EJP

回答

0

B的給定成員始終與base的偏移量相同,因爲baseB*

我認爲你錯過了一塊難題,當你通過BA*CB*時,test沒有通過對象本身的地址。
相反,它通過了B類型的子對象的地址。

示例使用類:

void test(B *base) { 
    std::cout << "test got " << base << std::endl; 
} 

int main() 
{ 
    BA ba; 
    CB cb; 
    std::cout << "&ba: " << &ba << std::endl; 
    std::cout << "ba's B subobject: " << static_cast<B*>(&ba) << std::endl; 
    test(&ba); 
    std::cout << "&cb: " << &cb << std::endl; 
    std::cout << "cb's B subobject: " << static_cast<B*>(&cb) << std::endl; 
    test(&cb); 
} 

對於我來說,這個印刷

&ba: 0x28cc78 
ba's B subobject: 0x28cc78 
test got 0x28cc78 
&cb: 0x28cc68 
cb's B subobject: 0x28cc70 
test got 0x28cc70 

兩個呼叫test通過B子對象的功能,每B物體看起來是一樣的,所以test根本不需要關心其他課程。

請注意,babaB子對象在同一個地方;這個特定的實現按照它們在繼承列表中指定的順序排列子對象,並且第一個位於派生對象中的第一個位置。
(這不是標準要求的,但它是一個非常常見的佈局。)

+0

謝謝你,這正是我錯過的難題!代碼演示確實有助於鞏固這一點。 – Xoanon

0

該標準沒有定義它。
實際的內存佈局通常取決於所選的ABI。
舉一個例子,其中一個是Itanium ABI
從介紹:

應用程序二進制接口C++程序,即,用戶C++代碼,並執行提供的系統和庫之間的對象代碼接口。
這包括C++數據對象的內存佈局,包括預定義和用戶定義的數據類型,以及內部編譯器生成的對象(如虛擬表)。它還包括函數調用接口,異常處理接口,全局命名和各種對象代碼約定。

Here是規則內存佈局的部分。

1

test必須使用B*類型的參數進行調用。編譯器知道,即使它看不到test的定義,因爲C++需要在任何引用它的翻譯單元中聲明一個函數。

C++允許正是因爲它知道如何一個CB*轉換爲B*你用指針調用testCB

如果結構沒有虛擬成員,轉換通常非常簡單。 CB對象將在某個偏移量處包含B對象。要將CB*轉換爲B*,只需要添加此偏移量。 test不需要知道該參數已轉換,甚至不需要知道即使存在CB

如果有虛擬功能,事情會稍微複雜一些。原則上,編譯器仍然以相同的方式調整CB*,但這不足以在運行時找到正確的虛擬函數。

儘管有各種方法來實現虛擬函數,並且C++標準沒有指定甚至推薦解決方案,但基本策略是在虛擬函數的對象中包含一個指向「vtable」的指針。 vtable是由實際對象實現的虛函數的一系列函數指針。因此,CB對象內的B對象將具有指向由CB定義的虛擬函數的虛表指針。這些函數必須調用this指向實際的CB對象,因此vtable或B對象還必須包含足夠的信息以在運行時從B*中派生出CB"。一種可能的解決方案是存儲調整(將從B*中減去),但還有其他各種可能性,具有不同的優點和缺點。

+0

感謝你的迴應,我甚至沒有想過如何實現虛擬功能等其他多態行爲:) – Xoanon

相關問題