之所以寫這篇《C++類的實例化對象的大小之sizeof()》,是因為在參加筆試的時候遇到如下這麼一道題,當時感覺就是這個一個坑,但,我還是義無反顧的跳了下去,因為存在知識點盲區啊。現,總結一下,你不知道的C++類的實例化對象的大小之sizeof()。
class D
{
public:
D()
{
}
virtual ~D()
{
}
private:
int a ;
char *p;
};
實例一:
class A
{
};
A a;
cout << sizeof(a) << endl;
運行結果:1
解釋:空類,沒有任何成員變量或函數,即沒有任何存儲內容;但是由A a可知,空類仍然可以實例化。一個類能夠實例化,編譯器就需給它分配內存空間,來指示類實例的地址。這裡編譯器默認分配了一個字節,以便標記可能初始化的類實例,同時使空類占用的空間最少(即1字節)。
實例二:
class B
{
private:
int a;
};
B b;
cout << sizeof(b) << endl;
運行結果:4
解釋:當類中有其它成員占據空間時,那一個字節就不算在內了,如本題:結果是4,而不是1+4=5。
實例三:
class BB
{
private:
int a ;
char b;
};
BB bb;
cout << sizeof(bb) << endl;
運行結果:8
解釋:什麼?怎麼會是8?不應該是4 + 1 = 5嗎?這裡考察了對齊,涉及到編譯器的優化。對於大多數CPU來說,CPU字長的整數倍操作起來更快,因此對於這些成員加起來不夠這個整數倍,有可能編譯器會插入多余的內容湊足這個整數倍;此外,有時候相鄰的成員之間也有可能因為這個目的被插入空白,這個叫做“補齊”(padding)。所以,C++標准緊緊規定成員的排列按照類定義的順序,但是不要求在存儲器中是緊密排列的。因此,如上的一個字節的char在存儲時被補全了,成為了4個字節。
實例四:
class C
{
private:
int a ;
char *p;
};
C c;
cout << sizeof(c) << endl;
運行結果:8
解釋:一般情況下,如果是指針,則無論指針指向的是什麼數據類型,都占4個字節的存儲空間。
實例五:
class D
{
public:
D()
{
}
virtual ~D()
{
}
private:
int a ;
char *p;
};
D d;
cout << sizeof(d) << endl;
運行結果:12
解釋:考察虛函數。當類含有虛函數時,(不論是自己的虛函數,還是繼承來的),那麼類中就有一個成員變量信息:虛函數指針(4個字節),這個指針指向一個虛函數表,虛函數表的第一項是類的typeinfo信息,之後的項為此類的所有虛函數的地址。
更進一步的解釋:當類中有虛函數的時候,編譯器會為類插入一個我們看不及愛你的數據並建立一個表。這個表就是虛函數表,那個我們看不見的數據就是指向虛函數表的指針——虛表指針。虛函數表就是為了保存類中的虛函數的地址。我們可以把虛函數表理解成一個數組,數組中的每個元素存放的就是類中虛函數的地址。當調用虛函數的時候,程序不是像普通函數那樣直接跳到函數的代碼處,而是先取出虛表指針即得到虛函數表的地址,根據這個來到虛函數表裡,從這個表理取出該函數的指針,最後調用該函數。
實例六:
class E
{
public:
E()
{
}
virtual ~E()
{
}
private:
int a ;
char *p;
static int b;
};
E e;
cout << sizeof(e) << endl;
運行結果:12
解釋:考察靜態成員變量的內存分配。由於靜態成員變量是在靜態存儲區分配空間的,它不屬於實例的一部分,因此類中的static成員變量不占據空間。
實例七:
class F:public E
{
public:
F()
{
}
~F()
{
}
private:
int c;
};
E e;
cout << sizeof(e) << endl;
運行結果:16
解釋:派生類對象的存儲空間 = 基類存儲空間 + 派生類特有的非static數據成員的空間
實例八:
class G: public virtual E
{
public:
G()
{
}
~G()
{
}
private:
int c;
};
G g;
cout << sizeof(g) << endl;
運行結果:20
解釋:如果是虛繼承的話,類對象的存儲空間大小 = 基類的存儲空間 + 派生類特有的非static數據成員的存儲空間 + 每一個類的虛函數存儲空間(這個是額外加的,即按照這個公式,sizeof(g) = 12(E基類的存儲空間) + 4(G特有的非static數據成員的存儲空間) + 4(E類的虛函數的存儲空間,如果E類中有多個虛函數,只算一次))。
實例九:
class H: public virtual E
{
public:
H()
{
}
~H()
{
}
virtual void GetValue()
{
}
private:
int c;
};
H h;
cout << sizeof(h) << endl;
運行結果:24
解釋:對比實例八,按照上面的解釋:類對象的存儲空間大小 = 基類的存儲空間 + 派生類特有的非static數據成員的存儲空間 + 每一個類的虛函數存儲空間(sizeof(h) = 12(E基類的存儲空間) + 4(G特有的非static數據成員的存儲空間) + 4(E類的虛函數的存儲空間,如果E類中有多個虛函數,只算一次)+ 4(H類的虛函數的存儲空間,如果H類中有多個虛函數,只算一次))。
如上,就是我對於這種類型的總結,這種問題只能出現一次!!!
《C++ 設計新思維》 下載見 http://www.linuxidc.com/Linux/2014-07/104850.htm
C++ Primer Plus 第6版 中文版 清晰有書簽PDF+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm
讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm
讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm
讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm
將C語言梳理一下,分布在以下10個章節中: