為什麼空類的大小不為0
文章目錄
1. 空類
所謂空類,意味著類中不攜帶任何資料。即該類中沒有非靜態成員函式、虛擬函式、虛基類等。理論上說,一個不攜帶任何資料的空類其所佔用的記憶體空間大小應該是零,因為它不需要儲存屬於任何附加物件的資料。
實際情況果真如我們猜想的那樣空類的大小為零嗎?讓我們拭目以待。
//test.cpp
#include <iostream>
using namespace std;
class Base{
};
int main()
{
cout<<"sizeof(Base): " <<sizeof(Base)<<endl;
return 0;
}
類Base是一個名副其實的空類,因為類內部不包含任何的成員和虛擬函式等。g++編譯該test.cpp程式(g++ test.cpp -o test),然後執行,得到的結果是:1。
對於空類,它有一個虛擬佔位符成員,該成員大小為1位元組,這個佔位符成員保證了每個物件應具有唯一的地址。因此,Base類的不同物件其地址空間是不相同的。如下:
#include <iostream>
using namespace std;
class Base{
};
int main ()
{
cout<<"sizeof(Base): "<<sizeof(Base)<<endl;
Base a, b;
if(&a == &b)
cout<<"The address is the same."<<endl;
else
cout<<"The address is different."<<endl;
cout<<"&a: "<<&a<<", "<<"&b: "<<&b<<endl;
return 0;
}
現定義兩個Base類物件,分別是a和b。然後分別對兩個物件的地址進行判斷,若相等,則列印“The address is the same.”反之,則列印“The address is different.” 編譯執行,得到的列印結果如下:
可看到,兩個物件所佔用的記憶體地址空間是不一樣的。
若對於一個空類,其所有物件佔用的記憶體地址空間一樣,會出現什麼問題?如下示例所示,分別定義兩個指向Base類型別的指標變數,然後分別初始化。
Base *p1 = new Base;
Base *p2 = new Base;
delete p1;
delete p2;
若各物件佔用的記憶體空間相同,那麼這裡delete p1、p2時候,會對同一記憶體空間釋放兩次,這是一個很嚴重的問題。
2. C++標準不允許空類大小為0
C++標準保證任何類的大小至少為一位元組。並且標準規定,任何物件都不應該與另外一個物件具有相同的記憶體地址。這樣做的理由是:
(1)保證了new始終會返回指向不同記憶體地址的指標。
(2)避免被零除。例如,指標算術運算(其中許多有編譯器自動完成)涉及除以sizeof(類名)。如下示例所示,若空類Base大小為0,那麼下面demo將會導致段錯誤。由於C++標準規定空類不為0,所以下面的demo將正常執行,sizeof(b)結果是10。
#define ARRAY_SIZE(arr) ( sizeof(arr) / sizeof(arr[0]) )
class Base{
};
Base b[10];
for(auto i = 0; i < ARRAY_SIZE(b); i++){
do something;
}
2.1 空的基類不會為派生類大小增1
對於空類,其大小為1,那我現在宣告一個派生類,繼承於基類。派生類物件的大小是不是等於派生類大小+(空)基類大小(一位元組)呢?其實不然,空基類不會為派生類的大小加一。如下示例:
#include <iostream>
using namespace std;
class Base{
};
class SubBase : public Base
{
char m_c;
};
int main()
{
cout<<"sizeof(SubBase): "<<sizeof(SubBase)<<endl;
return 0;
}
從列印結果可看到,對於之類SubBase的大小仍然為1位元組,這裡的1位元組是子類的成員變數m_c的大小。所以空基類不會為派生類的大小增加一。