2015-04-25 156 views
1

我的目標是創建共享某些常用數據的類層次結構類的實例。我創建了(有聯合)足夠的內存,以便可以在分配的內存中創建最大的實例。現在我想創建/交換類的實例,並在那裏的內存中使用「舊」數據。這是有效/合法的操作嗎?類層次結構的內存佈局

原始代碼使用一些MTP東西來創建聯合,目標是使用這個類層次作爲狀態機實現的核心。我只在這裏顯示包含問題的基本代碼。

我看到,如果基類不包含虛擬方法,但派生的方法確實存在問題,那麼這是一個問題。這是因爲vtable指針在內存前面(在x86/linux上使用gcc)。

簡單的問題:如果之前創建了基類的實例並且內存與該派生類的實例重用了內存,則派生類的實例是否可以從基類訪問數據?

class Base 
{ 
    public: 
     int a; 
     Base()=default; 
     Base(int _a):a(_a){} 

     void Print() { cout << "Value: " << a << endl; } 
}; 

class Derived1: public Base 
{ 
    public: 
     int d; 

     Derived1(): d(0x11223344){} 
}; 

union U 
{ 
    U(){} 
    Base base; 
    Derived1 derived1; 
} u; 

int main() 
{ 
    memset(&u, 0, sizeof(u)); 

    new (&u) Base(12345678); 
    u.base.Print(); 

    new (&u) Derived1; 
    u.base.Print(); 
} 
+0

做OOP時這是完全錯誤的做法。你應該使用'Base *'指針,它可以指向任何派生類。 – Barmar

+0

問題不在於如何通過指針訪問數據。問題在於層次結構中的佈局是否安全以便在層次結構中使用公共數據是安全的。原始代碼應該使用派生類中的數據。所提供的代碼僅用於查看效果。 – Klaus

回答

1

不,這將無法工作,因爲sandard說:

9.5/1:在工會,非靜態數據成員的至多一個可以隨時被激活也就是說,非靜態數據成員中至多有一個值可以隨時存儲在聯合中。

你試圖做的是未定義的行爲:

new (&u) Derived1; // RISKY !!! 

與放置新的您覆蓋,這是u中對象之前,沒有正確地破壞它。然後創建Derived1無論如何將創建自己的基地。如果以某種方式設法將舊值保存在內存中,它仍然是未定義的行爲:它可以工作或不工作,具體取決於編譯器的對象佈局和實現。

0

這是未定義的行爲 - 它可能似乎工作,但不是可移植的,不應該依賴。

聯盟只能有一個活躍成員;構建Derived1使之前的操作無效。