詳解C++中類的大小決定因數(shù)
在 C++ 中,類的大小(sizeof(ClassName)
)受多個(gè)因素影響,主要包括成員變量、對(duì)齊方式、繼承關(guān)系、虛函數(shù)表等。以下是影響類大小的關(guān)鍵因素:
1. 非靜態(tài)數(shù)據(jù)成員
類的大小主要取決于其非靜態(tài)成員變量的大小。
- 靜態(tài)成員變量 不影響 類的大?。ù鎯?chǔ)在全局?jǐn)?shù)據(jù)區(qū))。
- 非靜態(tài)成員變量 占用對(duì)象的存儲(chǔ)空間。
示例:
class A { int x; // 4 bytes char y; // 1 byte (可能有填充) }; std::cout << sizeof(A) << std::endl; // 可能是 8,而不是 5
由于結(jié)構(gòu)體對(duì)齊(Padding),sizeof(A)
可能是 8,而不是 5(見對(duì)齊規(guī)則)。
2. 數(shù)據(jù)對(duì)齊(Padding)
編譯器會(huì)對(duì)數(shù)據(jù)進(jìn)行內(nèi)存對(duì)齊,以提高 CPU 訪問(wèn)效率。
- 變量的地址需要符合其對(duì)齊要求(如
int
需要 4 字節(jié)對(duì)齊)。 - 可能會(huì)在成員之間填充字節(jié)(padding)。
示例:
class B { char a; // 1 byte int b; // 4 bytes, 但需要 4 字節(jié)對(duì)齊 char c; // 1 byte };
char a
占 1 字節(jié),但后面填充 3 字節(jié)(以滿足int b
的對(duì)齊)。int b
占 4 字節(jié)。char c
占 1 字節(jié),但后面填充 3 字節(jié)(類的大小需要是int
的倍數(shù))。- 總大小 =
1 + 3 (填充) + 4 + 1 + 3 (填充) = 12
字節(jié)。
可以使用 #pragma pack(1)
強(qiáng)制取消對(duì)齊,但可能會(huì)影響性能:
#pragma pack(1) class C { char a; int b; char c; }; #pragma pack() std::cout << sizeof(C) << std::endl; // 可能是 6 而不是 12
3. 虛函數(shù)(vtable 指針)
如果類包含虛函數(shù),則對(duì)象會(huì)存儲(chǔ)一個(gè)指向虛函數(shù)表(vtable)的指針。
- 該指針通常占 8 字節(jié)(64 位系統(tǒng))或 4 字節(jié)(32 位系統(tǒng))。
- 即使沒有成員變量,類也會(huì)占用指針大小的空間。
示例:
class D { virtual void func() {} }; std::cout << sizeof(D) << std::endl; // 8(64 位系統(tǒng)),4(32 位系統(tǒng))
僅包含虛函數(shù)的類,仍然占用 vtable 指針的大小。
如果是多重繼承且基類有虛函數(shù),每個(gè)基類都有自己的 vtable 指針:
class Base1 { virtual void foo() {} }; class Base2 { virtual void bar() {} }; class Derived : public Base1, public Base2 { }; std::cout << sizeof(Derived) << std::endl; // 16(64 位系統(tǒng)),8(32 位系統(tǒng))
Derived
類有兩個(gè) vtable 指針,每個(gè)占 8 字節(jié)(64 位系統(tǒng))。
4. 繼承
- 單繼承:子類包含父類的成員,并可能繼承 vtable 指針。
- 多繼承:可能會(huì)導(dǎo)致多個(gè) vtable 指針,增加對(duì)象大小。
- 虛繼承:由于需要額外存儲(chǔ) 虛基類指針(vbptr),可能增大對(duì)象大小。
普通繼承
class Base { int a; }; class Derived : public Base { char b; }; std::cout << sizeof(Derived) << std::endl; // 8(對(duì)齊后)
虛繼承
虛繼承需要維護(hù) 虛基類表指針(vbptr),導(dǎo)致額外的存儲(chǔ)開銷:
class Base { int a; }; class Derived1 : virtual public Base {}; class Derived2 : virtual public Base {}; class Final : public Derived1, public Derived2 {}; std::cout << sizeof(Final) << std::endl; // 額外增加 vbptr
Final
可能比普通繼承的大小更大,因?yàn)?nbsp;vbptr
需要額外存儲(chǔ)虛基類地址。
5. 空類
空類在 C++ 里不是零大小,通常是 1 字節(jié),以保證兩個(gè)對(duì)象的地址不同:
class Empty {}; std::cout << sizeof(Empty) << std::endl; // 1
即使類為空,C++ 也會(huì)給它分配 1 字節(jié),以確保不同對(duì)象有唯一地址。
如果空類有虛函數(shù),它仍然包含 vtable 指針:
class EmptyVirtual { virtual void func() {} }; std::cout << sizeof(EmptyVirtual) << std::endl; // 8(64 位系統(tǒng))
6. 位域(Bit Fields)
位域允許多個(gè)變量共享同一個(gè)字節(jié),減少存儲(chǔ)空間:
class BitField { unsigned int a : 1; // 1 bit unsigned int b : 2; // 2 bits unsigned int c : 3; // 3 bits }; std::cout << sizeof(BitField) << std::endl; // 4(存儲(chǔ)在一個(gè) int 中)
位域大小取決于其基礎(chǔ)類型(如 int
),如果跨越邊界,可能導(dǎo)致額外填充。
7. 編譯器優(yōu)化
不同編譯器可能優(yōu)化類的布局,如:
- 調(diào)整成員變量順序,減少填充字節(jié)。
- 自動(dòng)合并位域,提高空間利用率。
可以用 -Wpadded
(GCC/Clang)檢查填充:
g++ -Wpadded myfile.cpp
總結(jié)
影響因素 | 影響 |
---|---|
非靜態(tài)成員 | 直接影響類大小 |
數(shù)據(jù)對(duì)齊 | 可能增加填充字節(jié) |
虛函數(shù) | 增加 vtable 指針(通常 8 字節(jié)) |
繼承 | 繼承成員、vtable,可能增加 vbptr |
空類 | 不是 0,而是 1 字節(jié) |
位域 | 可能優(yōu)化存儲(chǔ),但依賴對(duì)齊 |
編譯器優(yōu)化 | 可能調(diào)整成員順序 |
如果你需要最小化類的大小,可以:
- 調(diào)整成員變量順序 以減少填充字節(jié)。
- 避免不必要的虛函數(shù)(若無(wú)多態(tài)需求)。
- 使用位域 以節(jié)省空間(但可能影響性能)。
- 使用
#pragma pack
取消對(duì)齊(但可能降低訪問(wèn)速度)。
到此這篇關(guān)于詳解C++中類的大小決定因數(shù)的文章就介紹到這了,更多相關(guān)C++類的大小決定因數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
精簡(jiǎn)高效的C#網(wǎng)站優(yōu)化經(jīng)驗(yàn)技巧總結(jié)
這篇文章主要為大家介紹了精簡(jiǎn)高效的C#網(wǎng)站優(yōu)化經(jīng)驗(yàn)技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04DevExpress的DateEdit設(shè)置顯示日期和時(shí)間的方法
本文主要介紹了DevExpress的DateEdit設(shè)置顯示日期和時(shí)間的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08winform 使用Anchor屬性進(jìn)行界面布局的方法詳解
這篇文章主要介紹了winform 使用Anchor屬性進(jìn)行界面布局的方法,有需要的朋友可以參考一下2013-12-12C#?計(jì)算DataTime的4種時(shí)間差的方法(相差天數(shù)、相差小時(shí)、相差分鐘、相差秒)
這篇文章主要介紹了C#?計(jì)算DataTime的4種時(shí)間差(相差天數(shù)、相差小時(shí)、相差分鐘、相差秒),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05C#實(shí)現(xiàn)操作windows系統(tǒng)服務(wù)(service)的方法
這篇文章主要介紹了C#實(shí)現(xiàn)操作windows系統(tǒng)服務(wù)(service)的方法,可實(shí)現(xiàn)系統(tǒng)服務(wù)的啟動(dòng)和停止功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04C#中Winform 實(shí)現(xiàn)Ajax效果自定義按鈕
這篇文章主要介紹了C#中Winform 實(shí)現(xiàn)Ajax效果自定義按鈕的相關(guān)資料,需要的朋友可以參考下2017-12-12C#實(shí)現(xiàn)在前端網(wǎng)頁(yè)彈出警告對(duì)話框(alert)的方法
這篇文章主要介紹了C#實(shí)現(xiàn)在前端網(wǎng)頁(yè)彈出警告對(duì)話框(alert)的方法,涉及C#通過(guò)自定義函數(shù)調(diào)用window.alert方法彈出對(duì)話框的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04清除aspx頁(yè)面緩存的程序?qū)崿F(xiàn)方法
這篇文章主要介紹了清除aspx頁(yè)面緩存的程序?qū)崿F(xiàn)方法,非常實(shí)用,需要的朋友可以參考下2014-08-08