一個空類class A{};的大小為什麼是1,因為如果不是1,當定義這個類的對象數組時候A objects[5]; objects[0]和objects[1]就在同一個地址處,就無法區分。
[cpp]
#include<iostream>
using namespace std;
class A
{
public:
virtual void aa(){}
private:
char k[3];
};
class B: public A
{
public:
virtual void bb(){}
};
int main()
{
cout<<"A's size is "<<sizeof(A)<<endl;
cout<<"B's size is "<<sizeof(B)<<endl;
return 0;
}
VS和gcc下執行結果:
A's size is 8
B's size is 8
說明:有虛函數的類有個virtual table(虛函數表),裡面包含了類的所有虛函數,類中有個virtual table pointers,通常成為vptr指向這個virtual table,占用4個字節的大小。成員類B public繼承於A,類B的虛函數表裡實際上有兩個虛函數A::aa()和B::bb(),類B的大小等於char k[3]的大小加上一個指向虛函數表指針vptr的大小,考慮內存對齊為8。
[cpp]
#include<iostream>
using namespace std;
class A
{
public:
virtual void aa(){}
private:
char k[3];
};
class B: public A
{
public:
//virtual void bb(){}
};
int main()
{
cout<<"A's size is "<<sizeof(A)<<endl;
cout<<"B's size is "<<sizeof(B)<<endl;
return 0;
}
VS和gcc下結果:
A's size is 8
B's size is 8
說明:類B看上去沒有虛函數,但實際上它有,只是沒有重寫,因為public繼承,所以有從A繼承過來的虛函數A::aa(),實際上類A和類B的虛函數表裡的函數都是A::aa()。
[cpp]
#include<iostream>
using namespace std;
class A
{
public:
virtual void aa(){}
virtual void aa2(){}
private:
char k[3];
};
class B: public A
{
public:
virtual void bb(){}
virtual void bb2(){}
};
int main()
{
cout<<"A's size is "<<sizeof(A)<<endl;
cout<<"B's size is "<<sizeof(B)<<endl;
return 0;
}
vs和gcc下
執行結果:
A's size is 8
B's size is 8
說明:一個類裡若有虛函數,無論有多少個虛函數都只有一個指向虛表的指針,虛表中的每一個表項保存著一個虛函數的入口地址。當調用虛函數時,先找到虛表中它對應的表項,找到入口地址再執行。對於直接繼承,無論類B中有無虛函數,由於它繼承了類A,且類A裡含有虛函數,因此如果類B有虛函數,那麼它和類A的是在同一個屬於類B的虛表裡。注意:類A裡的私有成員在類B裡仍占有內存。
[cpp]
#include<iostream>
using namespace std;
class A
{
public:
virtual void aa(){}
private:
char k[3];
};
class B: virtual public A
{
public:
//virtual void bb(){}
};
int main()
{
cout<<"A's size is "<<sizeof(A)<<endl;
cout<<"B's size is "<<sizeof(B)<<endl;
return 0;
}
vs和gcc下
執行結果:A's size is 8
B's size is 12
說明:類B裡包含,繼承的char k[3],繼承的虛函數,類B的虛函數表裡有A::aa(),因為是虛繼承,還有一個指向父類的指針,該指針為指向虛基類的指針(Pointer to virtual base class)。考慮內存對齊,總大小為12。
[cpp]
#include<iostream>
using namespace std;
class A
{
public:
virtual void aa(){}
private:
char k[3];
};
class B: public virtual A
{
public:
virtual void bb(){}
};
int main()
{
cout<<"A's size is "<<sizeof(A)<<endl;
cout<<"B's size is "<<sizeof(B)<<endl;
return 0;
}
VS執行結果:A's size is 8
B's size is 16
gcc執行結果:A's size is 8
B's size is 12
說明:對於虛繼承,類B虛繼承類A時,首先要通過加入一個指針來指向父類A,該指針被稱為虛基類指針。然後包含從父類繼承過來的3個char,再加上一個虛函數指針。考慮內存對齊,在gcc下結果是4+4+4=12。在VS下,結果是16,why?這一題和上一題區別只是在類B中添加了一個虛函數,但是兩個題目中類B都有虛函數表。在VS下調試查看匯編代碼,發現多出來的4字節什麼也沒有。
關於虛函數、多繼承、虛繼承,可以看《More Effective C++》:理解虛擬函數、多繼承、虛基類和RTTI所需的代價