日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ 中类的内存布局

發布時間:2025/7/14 c/c++ 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ 中类的内存布局 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在許多筆試面試中都會涉及到sizeof 運算符的求值問題。

這類問題主要分四類:

  • 基本數據類型,如int,bool,fload,long,long,int * 等,這一類比較簡單,但要注意x86和x64情況下的指針大小
  • 枚舉 enum。這個類型網絡上有說是1-4個byte,根據最大值決定的;也有說是sizeof(int)。我這邊個人使用 visual studio 2015 獲得的結果是4個byte
  • struct 和 union 組合類型。union 是取其中一個最大成員的size作為其size;struct 則要考慮對齊填充因素
  • class 類型,class 就稍微復雜點,不僅僅要考慮對齊填充因素,還要考慮繼承,虛繼承,虛函數等因素。
  • 下文主要講述class 的內存布局,稍帶介紹一下struct 的size。

    ?

    struct 的內存布局:

    struct 的內存對齊和填充概念學過C 的都應該知道一點。其實只要記住一個概念和三個原則就可以了:

    一個概念:

      自然對齊:如果一個變量的內存地址正好位于它長度的整數倍,就被稱做自然對齊。

      如果不自然對齊,會帶來CPU存取數據時的性能損失。(PS:具體應該與CPU通過總線讀寫內存數據的細節相關,具體沒有細究)

    三個原則:

  • struct 的起始地址需要能夠被其成員中最寬的基本數據類型整除;
  • struct 的size 也必須能夠被其成員中最寬的基本數據類型整除;
  • struct 中每個成員地址相對于struct 的起始地址的offset,必須是自然對齊的。
  • ?

    Class 的內存布局:

    在學習C++ 的class 的內存布局前,先介紹下文會被用到的Visual studio 中的編譯選項"/d1reportAllClassLayout" 和 "/d1reportSingleClassLayout[ClassName]"。

    這兩個編譯選項分別會輸出當前編譯單元中所以class 的內存布局和指定class 的內存布局。對于學習class 的內存布局很方便。

    ?

    關于一個class 的定義,在定義過程中涉及到的有:

      成員數據(靜態,非靜態)和成員函數(靜態,非靜態,virtual)。

    所有的成員函數都不會占用對象的存儲空間,無論是靜態,非靜態還是虛函數。

    而對于成員數據來說,只有非靜態的數據才會占用對象的存儲空間。

    這個很好理解,靜態成員數據和成員函數是屬于class 的,而非屬于具體的對象,所以只要維護一份內存就可以了,無需每個對象都拷貝一份。

    ?

    但是影響對象的大小的因素并不僅僅與看到的成員變量有關:

      非靜態成員變量,虛函數表指針(_vftprt),虛基類表指針(_vbtptr),上文的內存對齊

    ?

    • 空類

    class CEmpty{};

      對于空類,許多人想當然的認為大小應該是0。這是錯誤的,如果是正確的話,這個類可以被實例化成一個對象,且這個對象不占任何存儲空間,且可以有很多不占任何空間的對象,而且這個不占空間的對象還可以有指針,這樣就很奇怪了。

      所以正常編譯器會給空類分配1個byte 的空間用于標示。

      sizeof(CEmpty) = 1

    • 普通類

    class CBase { public:int m_ia;static int s_ib; private:void f();void g(); };

      其類的布局如下:

    class CBase size(4):+---0 | m_ia+---

      只有m_ia 成員,size 為4個byte。因為靜態數據成員和成員函數不占有對象空間。

    • 有虛函數的類

    class CBase { public:int m_ia; private:void f();void g();virtual void h(); };

      其類的布局如下:

    class CBase size(8):+---0 | {vfptr}4 | m_ia+---CBase::$vftable@:| &CBase_meta| 00 | &CBase::h

      可以看到該類的起始地址是放了一個"vfptr",這個指針用來指向該類的虛函數表。

    • 單一繼承的類(無虛函數)

    class CBase { public:int m_ia; private:void f();void g(); };class CChild :public CBase { public:int m_iChild; };

      類的布局如下:

    class CChild size(8):+---| +--- (base class CBase)0 | | m_ia| +---4 | m_iChild+---

      即派生類中拷貝了一份基類中的成員數據,所以size 為8個byte。

    • 單一繼承的類(含有虛函數)

    class CBase { public:int m_ia; public:virtual ~CBase();virtual void f();virtual void g(); };class CChild :public CBase { public:int m_iChild; public:virtual ~CChild();virtual void g(); };

      其類的布局如下:

    class CChild size(12):+---| +--- (base class CBase)0 | | {vfptr}4 | | m_ia| +---8 | m_iChild+---CChild::$vftable@:| &CChild_meta| 00 | &CChild::{dtor} 1 | &CBase::f 2 | &CChild::g

      可以看到派生類中只有一個"vfptr",但是虛函數表中的函數卻不同于基類中的函數,沒有重寫的虛函數沿用基類中的虛函數,而被重寫的虛函數則更新為派生類中的虛函數。

    • 多重繼承的類(基類都含有虛函數)

      

    class CBase1 { public:int m_i1; public:virtual ~CBase1();virtual void f1();virtual void g1(); };class CBase2 { public:int m_i2; public:virtual ~CBase2();virtual void f2();virtual void g2(); };class CChild :public CBase1, public CBase2 { public:int m_iChild; public:virtual ~CChild();virtual void f1();virtual void g2(); };

      其類的布局如下:

    class CChild size(20):+---| +--- (base class CBase1)0 | | {vfptr}4 | | m_i1| +---| +--- (base class CBase2)8 | | {vfptr} 12 | | m_i2| +--- 16 | m_iChild+---CChild::$vftable@CBase1@:| &CChild_meta| 00 | &CChild::{dtor} 1 | &CChild::f1 2 | &CBase1::g1 CChild::$vftable@CBase2@:| -80 | &thunk: this-=8; goto CChild::{dtor} 1 | &CBase2::f2 2 | &CChild::g2

      CChild 分別從CBase1 和 CBase 中繼承一個vfptr.

    • 菱形結構繼承的類(非虛繼承)

    class CBase { public:int m_iBase; public:virtual ~CBase();virtual void f0();virtual void g0();virtual void h0(); };class CChild1:public CBase { public:int m_iChild1; public:virtual ~CChild1();virtual void f0();virtual void h1(); };class CChild2:public CBase { public:int m_iChild2; public:~CChild2();void g0();void h1(); };class CGrandChild :public CChild1, public CChild2 { public:int m_iGrandChild; public:virtual ~CGrandChild();virtual void h0();virtual void h1();virtual void h2();virtual void f0(); };

      其類的布局如下:

    class CGrandChild size(28):+---| +--- (base class CChild1)| | +--- (base class CBase)0 | | | {vfptr}4 | | | m_iBase| | +---8 | | m_iChild1| +---| +--- (base class CChild2)| | +--- (base class CBase) 12 | | | {vfptr} 16 | | | m_iBase| | +--- 20 | | m_iChild2| +--- 24 | m_iGrandChild+---CGrandChild::$vftable@CChild1@:| &CGrandChild_meta| 00 | &CGrandChild::{dtor} 1 | &CGrandChild::f0 2 | &CBase::g0 3 | &CGrandChild::h0 4 | &CGrandChild::h1 5 | &CGrandChild::h2 CGrandChild::$vftable@CChild2@:| -120 | &thunk: this-=12; goto CGrandChild::{dtor} 1 | &thunk: this-=12; goto CGrandChild::f0 2 | &CChild2::g0 3 | &thunk: this-=12; goto CGrandChild::h0

      這種繼承是有風險的,即通過CGrandChild 去訪問m_iBase 時,容易造成二義性,需要使用"pGrandChild->CChild::m_iBase" 這種方法去訪問。

      為了避免這種問題,C++ 中有一種機制是虛繼承

    • 單一虛繼承

    class CBase { public:int m_iBase; public:virtual ~CBase();virtual void f0();virtual void g0();virtual void h0(); };class CChild1: virtual public CBase { public:int m_iChild1; public:virtual ~CChild1();virtual void f0();virtual void h1(); };

      其類的布局如下:

    class CChild1 size(24):+---0 | {vfptr}4 | {vbptr}8 | m_iChild1+--- 12 | (vtordisp for vbase CBase)+--- (virtual base CBase) 16 | {vfptr} 20 | m_iBase+---CChild1::$vftable@CChild1@:| &CChild1_meta| 00 | &CChild1::h1 CChild1::$vbtable@:0 | -41 | 12 (CChild1d(CChild1+4)CBase)CChild1::$vftable@CBase@:| -160 | &(vtordisp) CChild1::{dtor} 1 | &(vtordisp) CChild1::f0 2 | &CBase::g0 3 | &CBase::h0

      從布局中看,發現多了一個vbptr 指針,則是一個指向基類的虛基類指針;在派生類和虛基類之間又多了“vtordisp for vbase CBase”,vtordisp 并不是每個虛繼承的派生類都會生成的,關于這部分可以參考MSDN 中?vtordisp;在vtordisp 后面則是虛基類的一個拷貝。

    • 多重繼承的類(虛繼承)

    class CChild1 { public:int m_iChild1; public:virtual ~CChild1();virtual void f0();virtual void h1(); };class CChild2 { public:int m_iChild2; public:~CChild2();void g0();void h1(); };class CGrandChild :public CChild1, public CChild2 { public:int m_iGrandChild; public:virtual ~CGrandChild();virtual void h0();virtual void h1();virtual void h2();virtual void f0(); };

      virtual public Child1, public CChild2

        其類的布局如下:

    class CGrandChild size(28):+---0 | {vfptr}| +--- (base class CChild2)4 | | m_iChild2| +---8 | {vbptr} 12 | m_iGrandChild+--- 16 | (vtordisp for vbase CChild1)+--- (virtual base CChild1) 20 | {vfptr} 24 | m_iChild1+---

      

      public Child1, virtual public CChild2

        其類的布局如下:

    class CGrandChild size(20):+---| +--- (base class CChild1)0 | | {vfptr}4 | | m_iChild1| +---8 | {vbptr} 12 | m_iGrandChild+---+--- (virtual base CChild2) 16 | m_iChild2+---

      virtual?public Child1, virtual public CChild2

    class CGrandChild size(28):+---0 | {vfptr}4 | {vbptr}8 | m_iGrandChild+--- 12 | (vtordisp for vbase CChild1)+--- (virtual base CChild1) 16 | {vfptr} 20 | m_iChild1+---+--- (virtual base CChild2) 24 | m_iChild2+---

      通過上述虛繼承的情況來看,可以看出有虛繼承的派生類中,派生類和虛基類的數據是完全隔開的,先存放派生類自己的虛函數指針,虛基類指針和數據;然后有vtordisp 作為間隔;在存放虛基類的內容。

    • 菱形結構繼承的類(虛繼承)

    class CBase { public:int m_iBase; public:virtual ~CBase();virtual void f0();virtual void g0();virtual void h0(); };class CChild1 : virtual public CBase { public:int m_iChild1; public:virtual ~CChild1();virtual void f0();virtual void h1(); };class CChild2 : virtual public CBase{ public:int m_iChild2; public:virtual ~CChild2();virtual void g0();virtual void h1(); };class CGrandChild : public CChild1, public CChild2 { public:int m_iGrandChild; public:virtual ~CGrandChild();virtual void h0();virtual void h1();virtual void h2();virtual void f0(); };

      其類的布局如下:

    class CGrandChild size(40):+---| +--- (base class CChild1)0 | | {vfptr}4 | | {vbptr}8 | | m_iChild1| +---| +--- (base class CChild2) 12 | | {vfptr} 16 | | {vbptr} 20 | | m_iChild2| +--- 24 | m_iGrandChild+--- 28 | (vtordisp for vbase CBase)+--- (virtual base CBase) 32 | {vfptr} 36 | m_iBase+---

      有了上文的基礎,這個派生類的機構就不難理解了。

      

    轉載于:https://www.cnblogs.com/jiaochen/p/5524335.html

    總結

    以上是生活随笔為你收集整理的C++ 中类的内存布局的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。