2017-05-24 99 views
5

Próbuję odświeżyć niektóre rzeczy niskiego poziomu za pomocą wirtualnego stołu i dziedziczenia.Zrozumienie tabeli vtable w klasach pochodnych

Kiedy tworzysz nową klasę, dziedzicząc dwie klasy i dodając nowe funkcje wirtualne, gdzie dokładnie przechowywany będzie vptr?

Wydaje mi się, że kompilator wykonuje "optymalizację vptr" w tym przypadku. Próbuję to rozgryźć.

Przypuśćmy, że mamy następujące konstrukcjom:

struct A 
{ 
    int a; 
    virtual void fa(); 
}; 
struct B 
{ 
    double b; 
    virtual void fb(); 
}; 
struct C : A, B 
{ 
    char c; 
    virtual void fa(); 
    virtual void fb(); 
    virtual void fc(); 
}; 

w przypadku x86 i align = 4, A i B w pamięci będzie wyglądać następująco:

+------+------+ 
A: | vptr | a | 
    +------+------+ 
sizeof(A) = 4 + 4 = 8 

    +------+------+------+------+ 
B: | vptr  |  b  | 
    +------+------+------+------+ 
sizeof(B) = 8 + 8 = 16 

Ale gdy próbuję ponownie składaj C, otrzymuję to:

+------+------+------+------+------+------+------+ 
C: | vptr | a | vptr  |  b  | c | 
    +------+------+------+------+------+------+------+ 
but sizeof(C) = 32 

С с; 
(C*)&c; // 0x100 
(B*)&c; // 0x108 
(A*)&c; // 0x100 
&c.a; // 0x104 
&c.b; // 0x110 
&c.c; // 0x118 

Tak więc re jest vptr z C? Mogę przypuszczać, że kompilator scalania różnych tabel wirtualne, ale w takim razie dlaczego sizeof(C) powraca sizeof(A) + sizeof(B) + sizeof(alligned_char) + sizeof (vptr)

struct D : public C {} ma tę samą historię (dawny vptr z A i C.) - nie nie jest vptr z D.

Używany kompilator to msvc 2012 x86.

+1

czemu vptr w struct B muszą 8 bajtów? – walker

+1

@walker Wątpię, że tak. To prawdopodobnie trochę wyściółki. – luk32

+3

Możliwy duplikat [Ile vptr będzie obiektem klasy (używa dziedziczenia pojedynczego/wielokrotnego)?] (Https://stackoverflow.com/questions/3342035/how-many-vptr-will-a-object-of- classuses-one-multiple-dziedziczenie-have) – Andrew

Odpowiedz

4

Kompilator musi żonglować prostotą z tym, że klasy podstawowe muszą istnieć w obiekcie.

+------+---------+----+ 
| A | B  | C | 
+------+---------+----+ 

Więc

  • musi istnieć, jeżeli nie został wyprowadzony.
  • B musi istnieć tak, jakby nie pochodziło.
  • C ma nową wolność.

funkcje wirtualne z A i B zostaną załatane dla implementacji pochodnych klasy C. C doda wirtualne funkcje do (prawdopodobnie) istniejącego pierwszego elementu vtable A s.

baza vtable dla A

+------+ 
| A:fa | 
+------+ 

vtable dla A w klasie pochodnej C

+------+ 
| C:fa | // implemented by derived class. 
+------+ 
| C:fb | // any calls to fb need to be sent to `C`'s implementation 
+------+ 
| C:fc | // any calls to fc can be overridden by vtable. 
+------+ 

vtable dla B w klasie pochodnej C

+------+ 
| C:fb | // overridden, but no need to add fc, fa to this table. 
+------+ 

myślę wyrównania zasady są causi Rozmiar o wielkości C zostanie dopełniony, aby podwójne ustawienie czułości było poprawnie wyrównane (upewnij się, że macierz C jest prawidłowo wyrównana).

Wielkość B jest wielkość vptr (4) i wyściółki dla zapewnienia double jest w jednej linii (4) i wielkość double (8)

+1

Tak, okazało się to tylko wyrównaniem, nie nowy vptr. – renzo