歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

C++類的實例化對象的大小之sizeof()

之所以寫這篇《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個章節中:

  1. Linux-C成長之路(一):Linux下C編程概要 http://www.linuxidc.com/Linux/2014-05/101242.htm
  2. Linux-C成長之路(二):基本數據類型 http://www.linuxidc.com/Linux/2014-05/101242p2.htm
  3. Linux-C成長之路(三):基本IO函數操作 http://www.linuxidc.com/Linux/2014-05/101242p3.htm
  4. Linux-C成長之路(四):運算符 http://www.linuxidc.com/Linux/2014-05/101242p4.htm
  5. Linux-C成長之路(五):控制流 http://www.linuxidc.com/Linux/2014-05/101242p5.htm
  6. Linux-C成長之路(六):函數要義 http://www.linuxidc.com/Linux/2014-05/101242p6.htm
  7. Linux-C成長之路(七):數組與指針 http://www.linuxidc.com/Linux/2014-05/101242p7.htm
  8. Linux-C成長之路(八):存儲類,動態內存 http://www.linuxidc.com/Linux/2014-05/101242p8.htm
  9. Linux-C成長之路(九):復合數據類型 http://www.linuxidc.com/Linux/2014-05/101242p9.htm
  10. Linux-C成長之路(十):其他高級議題

Copyright © Linux教程網 All Rights Reserved