2013-04-13 100 views
0

考慮下面的類層次結構:虛擬多重繼承和指針

class A { 
int x; 
public: 
A(int X) : x(X) {} 
void setX(int x) { this->x = x; } 
}; 



class B : public virtual A { 
int y; 
public: 
B(int X, int Y) : A(X), y(Y) {} 
}; 

class C : public virtual A { 
int z; 
public: 
C(int X, int Z) : A(X), z(Z) {} 
}; 

class D : public C, public B { 
public: 
D(int x, int y, int z) : A(x), C(x,z), B(x,y) {} 
}; 

和以下主要:

int main (void) 
{ 
D x(2,3,4); 
A* temp1 = &x; 
B* temp2 = &x; 
C* temp3 = &x; 
} 

似乎temp1中,TEMP2和TEMP3都指向不同的地址.. B和C不應該共享相同的A對象嗎? 畢竟,每個C和B對象也是一個A,所以指針應該首先「看見」A對象。不。 另外,C指針包含X的地址..這是一個D對象。爲什麼?

這裏是存儲器映射:

&x  0x0036f828 {...} D * 
temp1 0x0036f838 {x=5 } A * 
temp2 0x0036f830 {y=3 } B * 
temp3 0x0036f828 {z=4 } C * 

回答

0

這只是對象的方式represented in memory

所以你的對象是這樣的:

+ 0x0036f828 
- D 
- int z (C) 
- int y (B) 
- int x (A) 

C++鑄造只是給出了對象的開始的偏移量。
所以你可以看到,偏移僅僅是類整數大小A,B,C(因爲這是它們所包含的)和類B,C的虛擬表。
d沒有成員,因此,它的偏移量是0

注意,C++編譯器設置由命令你實際編寫成員和基類的內存佈局。

所以,如果你想改變d基類的順序,那麼你會得到不同的結果:

class D : public B, public C 

現在將是佈局的第一類D.

+0

hehe感謝那個博客..有點相同的問題。所以我猜C是指向X,因爲D中的第一個對象是C。 – Rouki

+0

@Rouki我發現一個更好的示例/鏈接編輯了包含該鏈接的答案,它更好地解釋了內存佈局,並具有虛擬繼承的功能。是的,這是因爲C是佈局中的第一個基類。 –

+0

非常感謝你;) – Rouki

0

您的是b是正確的和C需要共享同一個目的。而這正是這裏會發生的事情。您所看到的地址實際上是每個班級所獨有的虛擬表的地址。在虛擬繼承的情況下,每個類的虛擬表將具有指向虛擬基類的指針,在這種情況下爲對象A.

因此,類B和C的虛擬表都會有一個指針指向對象A的相同地址。

+0

好的,爲什麼temp3與X有相同的地址? – Rouki

+0

這將是編譯器的優化,其中類D和C共享相同的虛擬表。這通常出現在派生類將使用相同虛擬表作爲其基礎之一的多重繼承中。在這種情況下,我必須提到,它不是一個真正的虛擬表,而是一個虛擬基表。虛擬表格與虛擬功能類似。 – Superman

0

這是事情會如何看,如果寫出來的使用普通的結構:

struct A { 
    int x; 
}; 

struct B { 
    A *ap; 
    int y; 
}; 

struct C { 
    A *ap; 
    int z; 
}; 

struct D { 
    C c; 
    B b; 
    A a; 
}; 

int main (void) 
{ 
    D x; 
    A* temp1 = &x.a; 
    B* temp2 = &x.b; 
    C* temp3 = &x.c; 
} 

由於BC使用虛擬連接,它們只包含一個指向基地的指針,而不包含實際的對象。您可以看到,由於c位於D的開頭,它們將具有相同的地址。

+0

謝謝..很好的例子。 – Rouki