2014-10-28 26 views
0

我在玩一些模板基礎代碼的參考。出於好奇,我想知道編譯器是否以某種方式優化了我在幕後的引用(因爲每次引用都與父類中的變量相同)。我決定一個簡單的sizeof()測試會告訴我。令我非常吃驚的是,僅僅引用了我繼承的類的3個float成員,將我的類內存佔用量提高了333%。這讓我大吃一驚,困惑不已。成員引用是否填充類?或者他們只是大於非會員參考?

經過一番搗鼓之後,我甚至不知道發生了什麼事情(但我懷疑我是啞巴)。

代碼:(代碼中:: Blocks的13.12使用GCC 4.8.2在Ubuntu 14.04編譯)

#include <iostream> 

template <typename T, unsigned int N> 
class FooArray{ 
public: 
    FooArray(){ 
     for (size_t i = 0; i < N; ++i) 
     { 
      m_data[i] = static_cast<T>(0); 
     } 
    } 
    FooArray(const T& _krv){ 
     for (size_t i = 0; i < N; ++i) 
     { 
      m_data[i] = _krv; 
     } 
    } 

    T& operator[](unsigned int _index); 

private: 
    T m_data[N]; 
}; 

template <typename T, unsigned int N> 
T& 
FooArray<T, N>::operator[](unsigned int _index) 
{ 
    return m_data[_index]; 
} 

class Inherit0Ref : public FooArray<float, 3>{}; 

class Inherit1Ref : public FooArray<float, 3> 
{ 
public: 
    Inherit1Ref():a((*this)[0]){} 
    float& a; 
}; 

class Inherit2Ref : public FooArray<float, 3> 
{ 
public: 
    Inherit2Ref():a((*this)[0]), b((*this)[1]){} 
    float& a; 
    float& b; 
}; 

class Inherit3Ref : public FooArray<float, 3> 
{ 
public: 
    Inherit3Ref():a((*this)[0]), b((*this)[1]), c((*this)[2]){} 
    float& a; 
    float& b; 
    float& c; 
}; 

class Inherit2RefMul : public FooArray<float, 3> 
{ 
public: 
    Inherit2RefMul():a((*this)[0]), b((*this)[1]){} 
    float& a, b; 
}; 

class Inherit3RefMul : public FooArray<float, 3> 
{ 
public: 
    Inherit3RefMul():a((*this)[0]), b((*this)[1]), c((*this)[2]){} 
    float& a, b, c; 
}; 

class FloatRef 
{ 
public: 
    FloatRef(float& _r):a(_r){} 

    float& a; 
}; 

class WrapFloat 
{ 
    float pad; 
}; 

class PadFloatRef 
{ 
public: 
    PadFloatRef():a(pad), pad(0.0f){} 

    float& a; 
    float pad; 
}; 

int main() 
{ 
    Inherit3Ref test; 
    test.a = 1.0f; 
    test.b = 2.0f; 
    test.c = 3.14f; 

    std::cout << test[0] << ", " << test[1] << ", " << test[2] << std::endl; 
    std::cout << "float size: " << sizeof(float) << std::endl; 
    std::cout << "float& size: " << sizeof(float&) << std::endl; 
    std::cout << "FloatRef size: " << sizeof(FloatRef) << std::endl; 
    std::cout << "WrapFloat size: " << sizeof(WrapFloat) << std::endl; 
    std::cout << "PadFloatRef size: " << sizeof(PadFloatRef) << std::endl; 
    std::cout << "FooArray<float, 3> size: " << sizeof(FooArray<float, 3>) << std::endl; 
    std::cout << "Inherit0Ref size: " << sizeof(Inherit0Ref) << std::endl; 
    std::cout << "Inherit1Ref size: " << sizeof(Inherit1Ref) << std::endl; 
    std::cout << "Inherit2Ref size: " << sizeof(Inherit2Ref) << std::endl; 
    std::cout << "Inherit3Ref size: " << sizeof(Inherit3Ref) << std::endl; 
    std::cout << "Inherit2RefMul size: " << sizeof(Inherit2RefMul) << std::endl; 
    std::cout << "Inherit3RefMul size: " << sizeof(Inherit3RefMul) << std::endl; 

    // Supposedly size 32, lets test assignment and access. 
    Inherit3RefMul testvar; 
    testvar.a = 5.0f; 
    testvar.b = 4.0f; 
    testvar.c = 3.142f; 

    // Interesting... 
    // testvar: 5, 0, 0 
    std::cout << "testvar: " << testvar[0] << ", " << testvar[1] << ", " << testvar[2] << std::endl; 

    return 0; 
} 

輸出:

1, 2, 3.14 
float size: 4 
float& size: 4 
FloatRef size: 8 
WrapFloat size: 4 
PadFloatRef size: 16 
FooArray<float, 3> size: 12 
Inherit0Ref size: 12 
Inherit1Ref size: 24 
Inherit2Ref size: 32 
Inherit3Ref size: 40 
Inherit2RefMul size: 32 
Inherit3RefMul size: 32 
testvar: 5, 0, 0 

很好奇。 :) 爲什麼包裹浮動引用(FloatRef)是包裹浮動(WrapFloat)大小的兩倍? 爲什麼使用一個浮點引用關鍵字(Inherit3RefMul)聲明3浮點引用會產生與具有2個引用而不是3(Inherit2Ref)的同一類相同的腳印? 爲什麼只有TestVar的第一個變量被填充,而所有3個測試都按預期填充? 有沒有一種方法來實現Inherit3Ref的功能,而沒有可笑的內存佔用40?

編輯:

請注意:

sizeof(float&) == 4

更新:

運行alignof(float&)返回4.我沒有看到一個浮動的原因[3]浮動& [3 ]類的大小爲40,當所有變量的大小爲4並且對齊爲4.

回答

2

編譯器必須添加填充,以確保任何成員變量適當對齊。 float具有大小4和對齊4,而引用和指針(對於任何)通常具有8(在64位計算機上)和對齊8。因此:

struct foo { 
    float m_data[3];    // size 3*4=12 begin=0 end=12 
    float&a;      // size 8  begin=16 end=24 NOTE: 4 unused bytes 
}; 

(另請注意,

float&a,b; 

相當於

float&a; 
float b; 

但不

typedef float&float_ref; 
float_ref a,b; 

這種表示法,因此最好避免爲清楚起見)

+0

感謝您參考聲明說明,我沒有意識到這種方式的語法。但是,請注意'sizeof(float&)'返回4.經過測試,'alignof(float&)'也返回4.這是否僅僅是我的另一個編程錯誤?看起來奇怪的是,包裝參考大小4使得課程大小爲8,而包裝浮動大小4則留下課程大小4. – Alterior 2014-10-29 02:17:10

+0

'sizeof(float&)'不會給你參考的大小,但大小提到的對象。引用是奇怪的野獸,幾乎總是衰減到所提到的類型。嘗試'sizeof(std :: reference_wrapper )'來獲取引用的實際大小。 – Walter 2014-10-29 09:57:47

+0

哇,這是一個很酷的類型...我喜歡標準庫!非常感謝你的幫助@沃爾特。 – Alterior 2014-10-29 12:23:16

2

浮動是4-byt es,一個引用是8個字節。所以3個浮點數組將是12個字節。唯一的非平凡位是你Inherit3Ref,這將是這樣的:

float[3]; 
*** padding to 8 byte boundary *** 
float &[3]; 

因此12個彩車+ 4個字節額外的填充+ 24個字節的引用,使40.如果你想避免填充字節,你可以使這個類打包 - 這應該讓你下降到36個字節。

不知道爲什麼你把它稱爲「雷人」 - 這當然必須是至少36 ...

+2

**你得到了錯誤**:填充不是16字節的邊界,而是8字節的邊界,因爲'alignof(float&)== 8'。恰巧在這種情況下,8字節邊界也是16字節邊界。 – Walter 2014-10-28 18:33:52

+0

@Walter oops。錯字? :) – Barry 2014-10-28 18:35:13

+0

@沃爾特你的答案更好,upvoted。 – Barry 2014-10-28 18:38:43