我們知道c++中乙個空類的大小為1個位元組,那麼如果乙個空類作為基類或者成員物件的時候會怎樣呢,是不是還是一定占用1個位元組呢?
c++中為保證同一型別的不同物件位址始終有別,要求任何物件或成員子物件,即使該型別是空的類型別(即沒有非靜態資料成員的 class 或 struct)也是如此。然而,基類子物件不受這種制約,而且可以完全從物件布局中被優化掉,若空基類之一亦為首個非靜態資料成員的型別或其型別的基類,則禁用空基優化,因為要求兩個同型別基類子物件在最終派生型別的物件表示中必須擁有不同位址。這種情況的典例是 std::reverse_iterator 的實現(派生自空基類 std::iterator),它持有其底層迭代器(亦派生自 std::iterator)為其首個非靜態資料成員。
空基類優化對於標準布局型別 (standardlayouttype)是被要求的,以維持指向標準布局物件的指標,用 reinterpret_cast 轉換後,還指向其首成員,這是標準要求標準布局型別「在同乙個類中宣告所有非靜態資料成員(c++11起 全在派生類或全在某個基類)」,並且「無與首個非靜態資料成員同型別的基類」的原因。
#include struct base {}; // 空類
struct derived1 : base ;
struct derived2 : base ;
struct derived3 : base ;
int main()
執行結果:
我們可以看到除了類derived1對空基類進行了優化,其他的都沒有,證明了上面提到的:若空基類之一亦為首個非靜態資料成員的型別或其型別的基類,則禁用空基優化,因為要求兩個同型別基類子物件在最終派生型別的物件表示中必須擁有不同位址。
空基類優化常用於具分配器的標準庫類(std::vector、std::function、std::shared_ptr 等),使得當分配器無狀態時可避免為其分配器成員占用任何額外儲存。這是通過將必要的資料成員之一(例如 vector 的 begin、end 或 capacity 指標)與分配器一起,在 boost::compressed_pair 的某種等價物中儲存而實現的。
但是從c++20起,若空成員子物件使用屬性 [[no_unique_address]],則允許像空基類一樣優化掉它們。取這種成員的位址會產生可能等於同乙個物件的某個其他成員的位址。[[no_unique_address]]指示此資料成員不需要具有不同於其類的所有其他非靜態資料成員的位址。這表示若該成員擁有空型別(例如無狀態分配器),則編譯器可將它優化為不佔空間,正如同假如它是空基類一樣。若該成員非空,則其中的任何尾隨填充空間亦可復用於儲存其他資料成員。
#include struct empty {}; // 空類
struct x ;
struct y ;
struct z ;
struct w ;
int main()
輸出(注意 需支援c++20的編譯器才會得到正確的輸出,這裡我也是截的網上的結果): C EBO 空基類最優化
ebo全稱empty base optimization 最近看effectivec 條款39,這個名詞讓我很陌生,學過之後發現也不是什麼新鮮玩意了。empty base optimization,空基類最優化,說到什麼是ebo,要先從一道面試題說起 class a cout cout 如果繼承空類...
模板與繼承之藝術 空基類優化
1 概念 c 中有 空 類的概念,這就意味著在執行期間其內部不好任何記憶體。只包含型別的成員函式 非虛成員函式和靜態資料成員的類為空類。非靜態的資料成員,虛函式和虛基類則在執行時期消耗儲存空間。2 空基類優化如下 include using namespace std class empty cla...
C 空類,空虛基類處理及類大小
對於乙個空類,編譯器會加入1byte的大小,使得這乙個類的兩個物件在記憶體中有獨一無二的位址。如下 class x class y public virtual x class z public virtual x class a public y,public z 每個類的大小 傳統編譯器對emp...