2011-11-08 18 views
24

Pytanie jest całkiem proste. Dla jasności, rozważmy przykład poniżej:Czy metody klasy zwiększają wielkość instancji klasy?

// 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; 
} 

Czy instancje f, b, d1 i d2 wszyscy zajmują tę samą ilość miejsca w pamięci? Jako rozszerzenie tego pytania, czy skopiowanie wystąpień o wartości Foo podczas ich przekazywania trwa teoretycznie dłużej niż Bar?

+0

99 metod? arrgh .. – Nim

+0

@Nim: Chciałem naprawdę zrozumieć.;) – Zeenobit

Odpowiedz

21

Dane tylko instancji zwiększają wielkość instancji klasy (we wszystkich znanych wersjach), z wyjątkiem tego, że jeśli dodasz funkcje wirtualne lub odziedziczysz po zajęciach z funkcjami wirtualnymi, wykonasz jednorazowe trafienie Wskaźnik v-table.

Ponadto, jak ktoś inny prawidłowo wspomina minimalny rozmiar klasy, jest to 1 bajt.

Kilka przykładów:

// 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 
{ 
}; 

implementacje kompilatora może obsługiwać wiele wirtualny dziedziczenia z wieloma wskaźnikami v-stole lub innej innymi metodami, tak też te będą miały wpływ na wielkość klasy.

Wreszcie opcje dopasowania danych elementu mogą mieć wpływ. Kompilator może mieć opcję lub #pragma, aby określić, że dane elementu powinny mieć adres początkowy, który jest wielokrotnością określonej liczby bajtów. Na przykład, przy ustawieniu na 4 bajtowych granicach i zakładając 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

Bliżej, ale puste typy zwykle zajmują co najmniej jeden bajt. Jest kilka kompilatorów, które sprawią, że będą one mniej niż jeden bajt. –

+4

@MooingDuck Musi być co najmniej jeden bajt na maszynach adresowanych bajtowo, aby dwa obiekty nie mogły mieć tego samego adresu. Kompilator nie podejmuje decyzji. (Stroustrup napisał to gdzieś, gdzie go czytam, nie pamiętam gdzie.) –

+0

@SethCarnegie: Tak, ale standard _also_ mówi, że kompilator może złamać wszystkie reguły, o ile wywoła takie same wywołania IO i niestabilne odczyt/zapis w tej samej kolejności z tymi samymi parametrami, co pozwala GCC czasami tworzyć obiekty mniejsze niż jeden bajt, o ile nie zmienia to wykonania programu. –

4

Strickly speaking, jest to zależne od implementacji. Ale liczba metod nie powinna zmieniać wielkości obiektów klasy. W przypadku metod innych niż wirtualne w pamięci obiektu nie ma nic, co odnosiło się do wskaźników metod. Jeśli masz metody wirtualne, każdy obiekt ma pojedynczy wskaźnik do tabeli vtable. Vtable rośnie, gdy dodajesz więcej metod, ale rozmiar wskaźnika pozostaje taki sam.

Dalsze informacje: dla metod nie-wirtualnych kompilator śledzi wskaźniki metod dla każdej klasy. gdy wywołujesz metodę inną niż wirtualna, kompilator przekazuje wskaźnik do metody, która wskazuje obiekt, jako ukryty parametr lub na stosie. W ten sposób metoda "zna" swój obiekt i ma dostęp do wskaźnika this. W przypadku metod wirtualnych wskaźnik metody jest w rzeczywistości indeksem do tabeli vtable, więc kompilator przekazuje this do pozycji dereferencjonowanej w tabeli vtable.

+0

dla metod innych niż wirtualne, kompilator śledzi wskaźniki metod dla każdej klasy. Czy to kompromis, który poświęca czas na przestrzeń? – Djvu

+0

@Djvu, nie, metody inne niż wirtualne są bardziej wydajne zarówno w czasie, jak iw przestrzeni. – ThomasMcLeod

3

Tak będą. Właściwie w twoim przypadku rozmiar będzie wynosił 1. W C++ klasa nawet bez żadnego elementu danych będzie miała rozmiar 1.

1

Foo i Bar każdy powinien być jeden bajt. Derived1 i Derived2 może być jednym lub dwoma bajtami.

Powód jest taki, że kompilator przechowuje wszystkie informacje statyczne w kodzie wykonywalnym, w tym wszystkie funkcje składowe. Gdzie twój kod wywołuje foo1 na instancji Foo, to po prostu dzwoni Foo::foo1(this), który jest taki sam dla każdego Foo w programie. funkcje wirtualne są wyjątkiem, ale nie dodają dodatkowego rozmiaru na funkcję składową, tylko jednorazowy dodatkowy rozmiar (zwykle 4/8 bajtów), jeśli w ogóle istnieją jakiekolwiek funkcje wirtualne.

Powiązane problemy