2013-10-25 74 views
1

看看這三個類(包括中省略):爲什麼繼承不會增加大小,但會員呢?

template<class T> struct A { 
    std::allocator<T> allocator; 
    T* ptr; 
}; 

template<class T> struct B { 
    T* ptr; 
    std::allocator<T> allocator; 
}; 

template<class T> struct C : std::allocator<T> { 
    T* ptr; 
}; 

int main(int argc, char **argv) { 
    std::cout << "A: " << sizeof(A<int>) << "\n"; 
    std::cout << "B: " << sizeof(B<int>) << "\n"; 
    std::cout << "C: " << sizeof(C<int>) << "\n"; 
} 

如果你問我,取決於對齊方式,無論是AB必須具有相同的大小C。然而,打印sizeof另有聲明:

A: 16 
B: 16 
C: 8 

這是爲什麼呢?

+3

'std :: allocator '本身沒有成員變量,因此如果您將它用作基類,則應用EBO(空基優化)。作爲會員,不允許這樣的優化(AFAIK)。 –

+0

@DanielFrey所以當創建一個容器時,我應該創建一個'detail'成員類,它包含所有數據成員,並從allocator模板參數繼承,以最大限度地減少容器的大小? – orlp

+0

@nightcracker:這就是幾乎所有標準庫類實現的方式... –

回答

5

通過C++ 11,9/4:

類型的完整對象和子對象構件應具有非零大小。

沒有這樣的限制適用於基類,只要每一個對象具有類型和地址的唯一。因此,只要「第一個」數據成員與基礎子對象的類型不同,基礎子對象的大小就可能爲零,既不完整也不是成員。

(我把 「第一次」 在引號,因爲有涉及訪問級別的併發症。)

事實上,1.8/5 – 6正規化以上:

5除非是有點 - 場(9.6),大多數派生對象應具有非零大小,並佔用一個或多個字節的存儲空間。 基類子對象可能具有零大小。可複製或標準佈局類型(3.9)的對象應占用連續的存儲字節。

6除非對象是零大小的位域或基類子對象,否則該對象的地址就是它佔據的第一個字節的地址。如果一個是另一個的子對象,或者如果至少有一個是大小爲零的基類子對象,並且它們的類型不同,則兩個不是位域的對象可能具有相同的地址;否則,他們應該有不同的地址。


下面是一個 「典型」 的實施std::vector,減去所有的名字改編:

template <typename T, typename Alloc> 
class vector 
{ 
    struct vbase : Alloc 
    { 
     T * data, * end, * capacity; 
     vbase(Alloc const & a) : Alloc(a), data(), end(), capacity() { } 
    }; 

    vbase impl; 

public: 
    vector(Alloc const & a = Alloc()) : impl(a) { } 

    T * data() const { return impl.data; } 
    T & operator[](size_t n) { return data()[n]; } 

    // ... 

    // use impl.allocate(), impl.construct() etc. 
}; 

這基本上確保了sizeof(vector<T>) == 3 * sizeof(T*)每當分配器是空的。

+0

我假設一個匿名類「impl」可以在沒有任何問題的情況下工作嗎? – orlp

+0

@nightcracker:你會如何爲匿名類編寫構造函數? –

+0

Derp。沒關係。我的大腦今天工作緩慢。 – orlp

相關問題