empty_bases
Microsoft 特定的
C++ 標準要求大部分衍生的物件必須具有非零大小,而且必須佔用一或多個位元組的儲存體。 因為需求只會延伸至大部分衍生的物件,所以基類子物件不受此條件約束限制。 空白基類優化 (EBCO) 會利用這個自由。 這會導致記憶體耗用量降低,進而改善效能。 Microsoft Visual C++ 編譯器過去對 EBCO 的支援有限。 在 Visual Studio 2015 Update 3 和更新版本中,我們已新增類別類型的新 __declspec(empty_bases)
屬性,以充分利用此優化。
重要
__declspec(empty_bases)
使用 可能會導致套用結構與類別配置的 ABI 中斷性變更。 當您使用此儲存體類別屬性時,請確定所有用戶端程式代碼都會使用與程式碼相同的結構和類別定義。
語法
__declspec( empty_bases )
備註
在 Visual Studio 中,沒有任何 __declspec(align())
或 alignas()
規格,空白類別的大小為 1 位元組:
struct Empty1 {};
static_assert(sizeof(Empty1) == 1, "Empty1 should be 1 byte");
類型為單一非靜態資料成員的 char
類別,大小也是 1 個位元組:
struct Struct1
{
char c;
};
static_assert(sizeof(Struct1) == 1, "Struct1 should be 1 byte");
在類別階層中結合這些類別,也會導致大小為 1 位元組的類別:
struct Derived1 : Empty1
{
char c;
};
static_assert(sizeof(Derived1) == 1, "Derived1 should be 1 byte");
此結果是工作的空白基類優化,因為它 Derived1
的大小為 2 個位元組:1 個位元組, Empty1
而 針對 為 Derived1::c
1 個位元組。 當有空類別鏈結時,類別配置也是最佳的:
struct Empty2 : Empty1 {};
struct Derived2 : Empty2
{
char c;
};
static_assert(sizeof(Derived2) == 1, "Derived2 should be 1 byte");
不過,Visual Studio 中的預設類別配置不會在多個繼承案例中利用 EBCO:
struct Empty3 {};
struct Derived3 : Empty2, Empty3
{
char c;
};
static_assert(sizeof(Derived3) == 1, "Derived3 should be 1 byte"); // Error
雖然 Derived3
大小可以是 1 位元組,但預設類別配置會導致大小為 2 個位元組。 類別配置演算法會在任兩個連續空白基類之間新增 1 個位元組的填補,有效地導致 Empty2
在 內 Derived3
耗用額外的位元組:
class Derived3 size(2):
+---
0 | +--- (base class Empty2)
0 | | +--- (base class Empty1)
| | +---
| +---
1 | +--- (base class Empty3)
| +---
1 | c
+---
當後續基類或成員子物件的對齊需求強制額外填補時,此次佳配置的效果會加總:
struct Derived4 : Empty2, Empty3
{
int i;
};
static_assert(sizeof(Derived4) == 4, "Derived4 should be 4 bytes"); // Error
型 int
別物件的自然對齊方式為 4 個位元組,因此必須在 之後 Empty3
新增 3 個位元組的額外填補,才能正確對齊 Derived4::i
:
class Derived4 size(8):
+---
0 | +--- (base class Empty2)
0 | | +--- (base class Empty1)
| | +---
| +---
1 | +--- (base class Empty3)
| +---
| <alignment member> (size=3)
4 | i
+---
預設類別配置的另一個問題是,在類別結尾的位移處可能會配置空的基類:
struct Struct2 : Struct1, Empty1
{
};
static_assert(sizeof(Struct2) == 1, "Struct2 should be 1 byte");
class Struct2 size(1):
+---
0 | +--- (base class Struct1)
0 | | c
| +---
1 | +--- (base class Empty1)
| +---
+---
雖然 Struct2
是最佳大小,但在 內的 Struct2
位移 1 配置, Empty1
但 的大小 Struct2
不會增加以加以考慮。 因此,針對 物件的陣列 A
Struct2
,的子物件 A[0]
位址會與 的位址 Empty1
A[1]
相同,這不應該如此。 如果在 Empty1
內的 Struct2
位移 0 配置,則不會發生此問題,因此會重迭 Struct1
子物件。
預設版面配置演算法尚未修改,以解決這些限制,並充分利用 EBCO。 這類變更會中斷二進位相容性。 如果類別的預設配置因 EBCO 而變更,則包含類別定義的每個物件檔案和程式庫都必須重新編譯,因此它們都同意類別配置。 這項需求也會延伸到從外部來源取得的程式庫。 這類程式庫的開發人員必須提供使用 和 不使用 EBCO 配置編譯的獨立版本,以支援使用不同版本的編譯器的客戶。 雖然我們無法變更預設版面配置,但可以使用新增 __declspec(empty_bases)
類別屬性,提供一種方法來根據每個類別變更配置。 使用這個屬性定義的類別可以充分利用 EBCO。
struct __declspec(empty_bases) Derived3 : Empty2, Empty3
{
char c;
};
static_assert(sizeof(Derived3) == 1, "Derived3 should be 1 byte"); // No Error
class Derived3 size(1):
+---
0 | +--- (base class Empty2)
0 | | +--- (base class Empty1)
| | +---
| +---
0 | +--- (base class Empty3)
| +---
0 | c
+---
的所有子物件 Derived3
都配置在位移 0,其大小是最佳 1 個位元組。 要記住的一個重要點是, __declspec(empty_bases)
只會影響套用類別的配置。 它不會以遞迴方式套用至基類:
struct __declspec(empty_bases) Derived5 : Derived4
{
};
static_assert(sizeof(Derived5) == 4, "Derived5 should be 4 bytes"); // Error
class Derived5 size(8):
+---
0 | +--- (base class Derived4)
0 | | +--- (base class Empty2)
0 | | | +--- (base class Empty1)
| | | +---
| | +---
1 | | +--- (base class Empty3)
| | +---
| | <alignment member> (size=3)
4 | | i
| +---
+---
雖然 __declspec(empty_bases)
已套用至 Derived5
,但不符合 EBCO 的資格,因為它沒有任何直接空白基類,因此沒有作用。 不過,如果改為套用至 Derived4
基類,這兩者 Derived4
都符合 EBCO 資格,而且 Derived5
會有最佳配置:
struct __declspec(empty_bases) Derived4 : Empty2, Empty3
{
int i;
};
static_assert(sizeof(Derived4) == 4, "Derived4 should be 4 bytes"); // No Error
struct Derived5 : Derived4
{
};
static_assert(sizeof(Derived5) == 4, "Derived5 should be 4 bytes"); // No Error
class Derived5 size(4):
+---
0 | +--- (base class Derived4)
0 | | +--- (base class Empty2)
0 | | | +--- (base class Empty1)
| | | +---
| | +---
0 | | +--- (base class Empty3)
| | +---
0 | | i
| +---
+---
由於所有物件檔案和程式庫都同意類別配置的需求, __declspec(empty_bases)
因此只能套用至您控制的類別。 它無法套用至標準程式庫中的類別,或套用至未使用 EBCO 配置重新編譯的程式庫中所包含的類別。
END Microsoft 特定的
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應