2012-10-20 52 views
4

如果我運行下面的代碼,我得到不同的地址打印。爲什麼?C++多繼承 - 編譯器修改我的指針

class Base1 { 
    int x; 
}; 

class Base2 { 
    int y; 
}; 

class Derived : public Base1, public Base2 { 

}; 

union U { 
    Base2* b; 
    Derived* d; 
    U(Base2* b2) : b(b) {} 
}; 

int main() 
{ 
    Derived* d = new Derived; 

    cout << d << "\n"; 
    cout << U(d).d << "\n"; 

    return 0; 
} 

更有趣的是,如果你反覆進出地址不斷遞增工會4,這樣

int main() 
{ 
    Derived* d = new Derived; 

    cout << d << "\n"; 
    d = U(d).d; 
    cout << d << "\n"; 
    d = U(d).d; 
    cout << d << "\n"; 

    return 0; 
} 

如果工會被修改這樣的,然後問題消失

union U { 
    void* v; 
    Base2* b; 
    Derived* d; 
    U(void* v) : v(v) {} 
}; 

此外,如果任一基類爲空,則問題消失。 這是一個編譯器錯誤?我想讓它獨自離開我的指針。

回答

3

如果我運行下面的代碼,我得到不同的地址打印。爲什麼?

由於Derived對象的Base2子對象不在Derived對象的開始。所以地址是不同的。當編譯器執行從Derived*Base2*的隱式強制轉換時,它需要調整地址。

鑑於Base1Base2類的定義,Derived類的兩個子對象不可能是在Derived物體的起始地址 - 有沒有房間位於該地址爲兩個子對象。

假設您有這樣的代碼:

Derived* d = new Derived; 

Base1* pb1 = d; 
Base2* pb2 = d; 

如何將它pb1pb2有可能指向同一個地址? pb1必須指向Base1::x項目,而pb2必須指向Base2::y項目(並且這些項目必須是不同的)。

更有趣的是,如果你反覆進出地址不斷遞增的工會去了4

因爲你從工會的d成員寫b成員,這是以後讀書未定義的行爲(您基本上在Base2*上執行類似reinterpret_cast<Derived*>()的操作)。

我想讓它離開我的指針地獄。

不是如果你想要一個Base2*指針。多重繼承使事情更加複雜 - 這就是爲什麼許多人建議避免它,除非絕對必要。

+0

不使用多重繼承不會使這個問題消失;如果Base類沒有虛函數,並且您將它們引入Derived類中,則會發生相同的情況。更重要的是,這是未定義的行爲,這就是故事的結局...... – enobayram

+0

好吧,我不想從聯盟中獲取Base2指針,所以問題出現在聯合構造函數中,因爲我正在初始化一個Base2指針,它被調整。如果我使用void *指針,那麼它會強制編譯器保留我給它的地址。 – Bob

2

工會構造從未初始化成員d

工會構造函數有一個bug,在那裏,而不是初始化部件B與B2的參數,它初始化b相本身

// b(b) should probably be b(b2) 
U(Base2* b2) : b(b) {} 

當你的第一個主要功能例子試圖構造一個U的實例,並打印成員d,它實際上是打印一個未定義的值,因爲成員d尚未初始化,並且不保證是可訪問的。

// U(d) doesn't construct member d, so .d returns an undefined value 
cout << U(d).d << "\n"; 

關於你提到的第二個主要功能例如

// d is set to a newly constructed instance of Derived 
Derived* d = new Derived; 

// current address of d is printed 
cout << d << "\n"; 

// a new instance of U is constructed. The address of member d will be in close 
// proximity to the newly initialized U instance, and is what will be printed 
d = U(d).d; 
cout << d << "\n"; 

// yet another new instance of U is constructed, and again, the address of member 
// d will be in close proximity to the newly initialized U instance, and is 
//what will be printed 
d = U(d).d; 
cout << d << "\n";