2011-11-08 39 views
24

這個問題很簡單。爲了清楚起見,可以考慮下面的例子:類方法是否會增加類實例的大小?

// Note that none of the class have any data members 
// Or if they do have data members, they're of equal size, type, and quantity 
class Foo { 
public: 
    void foo1(); 
    void foo2(); 
    // 96 other methods ... 
    void foo99(); 
}; 

class Bar { 
public: 
    // Only one method 
    void bar(); 
}; 

class Derived1 : public Foo { }; 
class Derived2 : public Bar { }; 

int main() { 
    Foo f; 
    Bar b; 
    Derived1 d1; 
    Derived2 d2; 
    return 0; 
} 

執行情況fbd1d2所有佔據的空間相同數量的內存?作爲對這個問題的延伸,理論上來說,在傳遞Foo的時候複製實例需要的時間比Bar長。

+0

99種方法? arrgh .. – Nim

+0

@Nim:我想真正明白這一點。;) – Zeenobit

回答

21

唯一實例數據增加了一個類的實例的大小(即我所知道的所有的實現),但如果從虛擬函數的類添加虛函數或繼承,那麼你採取一次性擊中了v表指針。

而且,別人正確地提到了一個類的最小大小爲1個字節。

一些例子:

// size 1 byte (at least) 
class cls1 
{ 
}; 

// size 1 byte (at least) 
class cls2 
{ 
    // no hit to the instance size, the function address is used directly by calling code. 
    int instanceFunc(); 
}; 

// sizeof(void*) (at least, for the v-table) 
class cls3 
{ 
    // These functions are indirectly called via the v-table, a pointer to which must be stored in each instance. 
    virtual int vFunc1(); 
    // ... 
    virtual int vFunc99(); 
}; 

// sizeof(int) (minimum, but typical) 
class cls4 
{ 
    int data; 
}; 

// sizeof(void*) for the v-table (typical) since the base class has virtual members. 
class cls5 : public cls3 
{ 
}; 

編譯器實現可以處理具有多個v表指針或其他其他方法的多個虛擬繼承,所以這些也都會對類尺寸效應。

最後,構件數據對齊選項可以具有影響。編譯器可能有一些選項或者#pragma來指定成員數據的起始地址應該是指定字節數的倍數。例如,具有4個字節邊界對齊並假設sizeof(int) = 4

// 12 bytes since the offset of c must be at least 4 bytes from the offset of b. (assuming sizeof(int) = 4, sizeof(bool) = 1) 
class cls6 
{ 
    int a; 
    bool b; 
    int c; 
}; 
+0

更接近,但空的類型通常至少需要一個字節。很少有編譯器會使這些字節少於一個字節。 –

+4

@MooingDuck它必須在字節可尋址的機器上至少有一個字節,以便兩個對象不能有相同的地址。編譯器無法做出決定。 (Stroustrup寫這個地方是我讀的地方,但是不記得它在哪裏) –

+0

@SethCarnegie:是的,但是標準的_also_說編譯器可以打破所有的規則,只要它使得相同的IO調用和volatile以相同的順序讀取/寫入相同的參數,這允許GCC有時使對象小於一個字節,只要這不會改變程序的執行。 –

4

嚴格來說,這是依賴於實現的。但是方法的數量不應該改變類對象的大小。對於非虛擬方法,對象內存中沒有任何與方法指針相關的內容。如果你有虛擬方法,那麼每個對象都有一個指向虛表的指針。添加更多方法時,vtable會增長,但指針大小保持不變。

進一步信息:用於非虛擬方法,編譯器跟蹤方法指針爲每個類。當您調用非虛方法時,編譯器會將指向指向該對象的方法的指針傳遞給隱藏參數或堆棧。這就是一種方法如何「知道」它的對象,並訪問指針this。對於虛擬方法,方法指針實際上是vtable的索引,因此編譯器將this傳遞給vtable中的解除引用的條目。

+0

對於非虛方法,編譯器跟蹤每個類的方法指針。這是一個犧牲時間換取空間的權衡嗎? – Djvu

+0

@Djvu,不,非虛擬方法在時間和空間上都更高效。 – ThomasMcLeod

3

是他們會。實際上,在你的情況下,大小將爲1.在C++中,即使沒有任何數據成員,類也將具有大小1.

1

FooBar應該各自爲一個字節。 Derived1Derived2可能是一個或兩個字節。

原因是編譯器存儲在可執行代碼的所有靜態信息,包括所有的成員函數。當你的代碼上的Foo一個實例調用foo1,它只是調用Foo::foo1(this),這是在程序中的每個Foo相同。虛擬函數是一個例外,但不會爲每個成員函數添加額外的大小,如果有虛擬函數,則不會增加一次性額外大小(通常爲4/8字節)。