更多 C++ 慣用法/空基最佳化
最佳化空類型別的成員資料的儲存
- EBCO:空基類最佳化
- 空成員最佳化
空類在 C++ 中時常出現。C++ 要求空類具有非零大小,以確保物件標識。例如,下面的 EmptyClass 陣列必須具有非零大小,因為由陣列下標標識的每個物件都必須是唯一的。如果 sizeof(EmptyClass) 為零,指標運算將失效。通常,此類類的尺寸為 1。
class EmptyClass {};
EmptyClass arr[10]; // Size of this array can’t be zero.
當同一個空類作為其他類的成員出現時,它可能會佔用超過一個位元組。編譯器會根據體系結構要求,根據成員大小對資料進行對齊。填充位元組是為了滿足這些要求而插入的,但沒有其他用途。避免記憶體浪費通常是可取的,在某些情況下甚至至關重要:從空基類派生具有任何型別單一成員的結構,或包含空成員,通常會使其大小增加 2 倍,因為存在對齊要求。
C++ 對空類被繼承時的處理方式有特殊豁免。編譯器可以扁平化繼承層次結構,使得空基類不會佔用空間。例如,在下面的示例中,在 32 位體系結構上 sizeof(AnInt) 為 4,而 sizeof(AnotherEmpty) 為 1 位元組,即使它們都繼承自 EmptyClass
class AnInt : public EmptyClass
{
int data;
}; // size = sizeof(int)
class AnotherEmpty : public EmptyClass {}; // size = 1
EBCO 以系統化的方式利用了這種豁免。將空類從成員列表中移到基類列表中可能並不理想,因為這可能會暴露原本對使用者隱藏的介面。例如,以下應用 EBCO 的方式將應用最佳化,但可能會產生不良副作用:函式的簽名(如果 E1、E2 中有任何函式)現在對 Foo 類的使用者可見(儘管他們無法呼叫它們,因為是私有繼承)。
class E1 {};
class E2 {};
// **** before EBCO ****
class Foo {
E1 e1;
E2 e2;
int data;
}; // sizeof(Foo) = 8
// **** after EBCO ****
class Foo : private E1, private E2 {
int data;
}; // sizeof(Foo) = 4
使用 EBCO 的一種實際方法是將空成員合併成一個單一成員,從而扁平化儲存。下面的模板 BaseOptimization 對其前兩個型別引數應用 EBCO。上面的 Foo 類已經過重寫以使用它。
template <class Base1, class Base2, class Member>
struct BaseOptimization : Base1, Base2
{
Member member;
BaseOptimization() {}
BaseOptimization(Base1 const& b1, Base2 const & b2, Member const& mem)
: Base1(b1), Base2(b2), member(mem) { }
};
class Foo {
BaseOptimization<E1, E2, int> data;
}; // sizeof(Foo) = 4
使用這種技術,Foo 類的繼承關係沒有發生變化。而且,由於 BaseOptimization 沒有宣告任何成員函式,因此它也避免了意外覆蓋基類函式的問題。請注意,在上面所示的方法中,至關重要的是基類之間不衝突。也就是說,Base1 和 Base2 是獨立層次結構的一部分。
警告
物件標識問題似乎在不同編譯器之間並不一致。空物件的地址可能相同也可能不同。例如,考慮以下情況。
template <class Base1, class Base2, class Member>
struct BaseOptimizationWithFunctions : Base1, Base2
{
Member member;
BaseOptimizationWithFunctions() {}
BaseOptimizationWithFunctions(Base1 const& b1, Base2 const & b2, Member const& mem)
: Base1(b1), Base2(b2), member(mem) { }
Base1 * first() { return this; }
Base2 * second() { return this; }
};
由 first 和 second 成員函式返回的指標在某些編譯器上可能相同,而在其他編譯器上可能不同。有關更多討論,請參見 StackOverflow
- boost::compressed_pair 利用這種技術來最佳化對的尺寸。
- C++03 對 unique_ptr 的模擬也使用這種慣用法。