下面我們來看虛繼承。首先看看這C020類,它從C010虛繼承:}
struct C010
{
C010() : c_(0x01) {}
void foo() { c_ = 0x02; }
char c_;
};
struct C020 : public virtual C010
{
C020() : c_(0x02) {}
char c_;
};
運(yùn)行如下代碼,查看對象的內(nèi)存布局:
PRINT_SIZE_DETAIL(C020)
結(jié)果為:
The size of C020 is 6
The detail of C020 is c0 c2 45 00 02 01
很明顯對象的起始處是一個指針,然后是子類的成員變量,接下來是父類的成員變量。和以前的討論不同的是由于使用了虛繼承,父類的成員變量被放到了最后面。
運(yùn)行如下的代碼:
C020 c020;
c020.C010::c_ = 0x04;
由于子類中的變量和父類中的變量重名,所以我們必須用這種方式來訪問屬于父類的成員變量,普通情況下不需要這種寫法。我們看看后面這行代碼對應(yīng)的匯編代碼:
0042387E mov eax,dword ptr [ebp FFFFF82Ch]
00423884 mov ecx,dword ptr [eax 4]
00423887 mov byte ptr [ebp ecx FFFFF82Ch],4
前面說過對象的起始是一個指針,第1行指令取到這個指針的值,第2行把這個指針指向的地址后移4字節(jié)后的值(做為一個4字節(jié)的值)取出來。執(zhí)行完這句我們看看ecx寄存器,可知取出來的值為5。最后一行是真正的賦值指令,它通過在對象的起始處(即[ebp FFFFF32Ch])加上ecx中的值做偏移值(即5)來得到賦值的目的地址。接合前面的對象布局輸出,我們可以發(fā)現(xiàn)從對象起始地址開始加5字節(jié)的偏移值,剛好得到父類的成員變量的地址。這樣我們可以大致分析出直接虛繼承的子類的對象布局。
|子類5 |父類1 ?。?BR> |偏移值指針4,5|子類成員變量1|父類成員變量1|
(注:第一個數(shù)字為所在區(qū)域的長度(字節(jié)數(shù)),偏移值指針后的第二個數(shù)字為該指針指向的偏移值。后同。)
通過查看內(nèi)存可以發(fā)現(xiàn)偏移值指針指向的內(nèi)存前4字節(jié)為0,我不知道它的具體的用途是什么。接下來的4字節(jié)是一個32位的整數(shù),也就是真正的偏移值。即從子類的起始位置到被虛繼承的父類的起始位置的偏移值,在我們前面的例子中這個值為5(一個指針加一個char成員變量)。
通過這個分析我們可以看到在虛承繼的情況下,通過子類的對象訪問父類的普通成員變量的效率是相當(dāng)?shù)偷?。如果必須用到虛繼承,也應(yīng)該盡量不要在父類中放置普通成員變量(靜態(tài)成員變量不受影響)。
struct C010
{
C010() : c_(0x01) {}
void foo() { c_ = 0x02; }
char c_;
};
struct C020 : public virtual C010
{
C020() : c_(0x02) {}
char c_;
};
運(yùn)行如下代碼,查看對象的內(nèi)存布局:
PRINT_SIZE_DETAIL(C020)
結(jié)果為:
The size of C020 is 6
The detail of C020 is c0 c2 45 00 02 01
很明顯對象的起始處是一個指針,然后是子類的成員變量,接下來是父類的成員變量。和以前的討論不同的是由于使用了虛繼承,父類的成員變量被放到了最后面。
運(yùn)行如下的代碼:
C020 c020;
c020.C010::c_ = 0x04;
由于子類中的變量和父類中的變量重名,所以我們必須用這種方式來訪問屬于父類的成員變量,普通情況下不需要這種寫法。我們看看后面這行代碼對應(yīng)的匯編代碼:
0042387E mov eax,dword ptr [ebp FFFFF82Ch]
00423884 mov ecx,dword ptr [eax 4]
00423887 mov byte ptr [ebp ecx FFFFF82Ch],4
前面說過對象的起始是一個指針,第1行指令取到這個指針的值,第2行把這個指針指向的地址后移4字節(jié)后的值(做為一個4字節(jié)的值)取出來。執(zhí)行完這句我們看看ecx寄存器,可知取出來的值為5。最后一行是真正的賦值指令,它通過在對象的起始處(即[ebp FFFFF32Ch])加上ecx中的值做偏移值(即5)來得到賦值的目的地址。接合前面的對象布局輸出,我們可以發(fā)現(xiàn)從對象起始地址開始加5字節(jié)的偏移值,剛好得到父類的成員變量的地址。這樣我們可以大致分析出直接虛繼承的子類的對象布局。
|子類5 |父類1 ?。?BR> |偏移值指針4,5|子類成員變量1|父類成員變量1|
(注:第一個數(shù)字為所在區(qū)域的長度(字節(jié)數(shù)),偏移值指針后的第二個數(shù)字為該指針指向的偏移值。后同。)
通過查看內(nèi)存可以發(fā)現(xiàn)偏移值指針指向的內(nèi)存前4字節(jié)為0,我不知道它的具體的用途是什么。接下來的4字節(jié)是一個32位的整數(shù),也就是真正的偏移值。即從子類的起始位置到被虛繼承的父類的起始位置的偏移值,在我們前面的例子中這個值為5(一個指針加一個char成員變量)。
通過這個分析我們可以看到在虛承繼的情況下,通過子類的對象訪問父類的普通成員變量的效率是相當(dāng)?shù)偷?。如果必須用到虛繼承,也應(yīng)該盡量不要在父類中放置普通成員變量(靜態(tài)成員變量不受影響)。