考慮這樣一個類b,依次包含一個虛指針、char、類a、int、double數組
struct a {
char c;//1+7
double d;//8
};//16
struct b {
char c;
a s;
int i;
double d[3];
virtual ~b() {}
};
按照道理來講,虛指針vptr佔用4字節,然後char c 佔用1個字節,再補3個字節,使得a類的開始偏移量是a類最大成員double d的整數倍,然後a s佔用16字節,int i佔用4個字節,再補4個字節,使得double d[3]開始的位置是8的整數倍,最會double d[3]佔用24個字節,最終就是4+(1+3)+16+(4+4)+(8*3)=56個字節,但是實際上是64個字節。
爲什麼呢?打開類b的一個對象的內存分配看看:
int main() {
b test;
test.c = ' ';
test.i = 1;
test.s.c = ' ';
test.s.d = 4;
test.d[0] = (test.d)[1] = (test.d)[2] = 2;
b *T= &test;
cout << sizeof b << endl;
}
可以看到,虛函數指針後面補了4個字節,使得char c不得不補7個字節而不是3個字節, 以使得s的起始偏移量是8的倍數,所以多出來的8個字節在這裏。
初步得出結論:虛表指針不跟其它成員對齊一起計算,其它成員的第一個起始位置應該是成員中體積最大的成員的整數倍,此例中b中最大的成員是double,其包含的類a中最大的成員也是double,所以第一個成員char c的起始位置應該是4+4=8。
那麼,如果刪掉成員s或者數組double d[3]呢?
struct a {
char c;//1+7
double d;//8
};//16
struct b {
char c;
// a s;
int i;
double d[3];
virtual ~b() {}
};
int main() {
b test;
test.c = ' ';
test.i = 1;
//test.s.c = ' ';
//test.s.d = 4;
test.d[0] = (test.d)[1] = (test.d)[2] = 2;
b *T= &test;
cout << sizeof b << endl;
}
註釋掉a類的成員,輸出40,內存分佈如下:
虛函數指針依舊補了4個字節。註釋掉double d[3],輸出40,內存分佈如下:
同樣地,虛函數指針補了4個字節 ,同時最後補了4個字節以成爲8的倍數。如果把兩個8字節的都註釋掉,即類b是這樣:
struct b {
char c;
int i;
virtual ~b() {}
};
int main() {
b test;
test.c = ' ';
test.i = 1;
b *T= &test;
cout << sizeof b << endl;
}
輸出就是喜聞樂見的12了。
說明初步的結論是正確的。也說明,當存在虛函數的時候,虛函數跟其它成員分開對齊,可以把其它成員當做一個類,這樣就轉換成爲類中包含類的對齊問題了。