Domanda

Sono "giocando" con l'ereditarietà virtuale in C ++, e voglio sapere come un oggetto di classe è disposto. Ho queste tre classi:

class A {
private:
    int a;
public:
    A() {this->a = 47;}
    virtual void setInt(int x) {this->a = x;}
    virtual int getInt() {return this->a;}
    ~A() {this->a = 0;}
};

class B {
private:
    int b;
public:
    B() {b = 48;}
    virtual void setInt(int x) {this->b = x;}
    virtual int getInt() {return this->b;}
    ~B() {b = 0;}
};

class C : public A, public B {
private:
    int c;
public:
    C() {c = 49;}
    virtual void setInt(int x) {this->c = x;}
    virtual int getInt() {return this->c;}
    ~C() {c = 0;}
};

(penso che siano corrette: p)

ho usato -fdump-class-hierarchy con g ++, e ho ottenuto questo

Vtable for A
A::_ZTV1A: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1A)
16    A::setInt
24    A::getInt

Class A
   size=16 align=8
   base size=12 base align=8
A (0x10209fb60) 0
    vptr=((& A::_ZTV1A) + 16u)

Vtable for B
B::_ZTV1B: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1B)
16    B::setInt
24    B::getInt

Class B
   size=16 align=8
   base size=12 base align=8
B (0x1020eb230) 0
    vptr=((& B::_ZTV1B) + 16u)

Vtable for C
C::_ZTV1C: 8u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1C)
16    C::setInt
24    C::getInt
32    (int (*)(...))-0x00000000000000010
40    (int (*)(...))(& _ZTI1C)
48    C::_ZThn16_N1C6setIntEi
56    C::_ZThn16_N1C6getIntEv

Class C
   size=32 align=8
   base size=32 base align=8
C (0x1020f5080) 0
    vptr=((& C::_ZTV1C) + 16u)
  A (0x1020ebd90) 0
      primary-for C (0x1020f5080)
  B (0x1020ebe00) 16
      vptr=((& C::_ZTV1C) + 48u)

Ora che cosa diavolo sono quelli (int (*)(...))-0x00000000000000010 e C::_ZThn16_N1C6setIntEi and (int (*)(...))0 ?? Qualcuno può spiegare la discarica?

Grazie.

È stato utile?

Soluzione

Io non sono sicuro al 100% che questa risposta è corretta, ma ecco la mia ipotesi migliore.

Quando si dispone di una classe che eredita moltiplicano e non virtualmente, il layout della classe è di solito un oggetto completo del primo tipo di base, quindi un oggetto completo del secondo tipo di base, quindi i dati per l'oggetto stesso. Se si guarda alla B, si può vedere il puntatore vtable per l'oggetto A, e se si guarda in C si può vedere che non c'è puntatori nella vtable per entrambi gli oggetti A e B.

Poiché gli oggetti sono disposti in questo modo, significa che se si dispone di un puntatore B* indicando un oggetto C, il puntatore sarà effettivamente non essere alla base dell'oggetto; piuttosto si punterà qualche parte nel mezzo. Ciò significa che, se mai hai bisogno di lanciare l'oggetto a un A*, avrete bisogno di regolare il puntatore B* una certa quantità di saltare indietro all'inizio dell'oggetto. Per fare questo, le esigenze del compilatore per codificare da qualche parte il numero di byte è necessario saltare indietro per arrivare al punto di partenza dell'oggetto. Credo che il primo (int(*)(...)) è in realtà solo un certo numero di byte è necessario guardare per raggiungere l'inizio dell'oggetto. Se si noterà, per la vtable A questo indicatore è 0 (in quanto il vtable per A si trova all'inizio di un oggetto, e lo stesso vale per la vtable B (dal momento che vive anche ad inizio dell'oggetto. Tuttavia , si noti che il vtable C ha due parti - la prima parte è il vtable per A, e la sua prima voce pazza è pari a zero, nonché (dal momento che se siete al vtable A, non è necessario fare alcuna regolazione). Tuttavia, dopo la prima metà di questo tavolo è quello che sembra essere il vtable B, e notare che la sua prima voce è il valore esadecimale -0x10. Se si guarda la disposizione oggetto C, si noterà che il puntatore vtable B è di 16 byte dopo che il puntatore vtable A. Questo valore -0x10 potrebbe essere la correzione di offset è necessario per saltare indietro sopra il puntatore vtable B per tornare alla radice dell'oggetto.

La seconda voce folle di ogni vtable sembra essere un puntatore alla vtable stesso. Si noti che è sempre uguale all'indirizzo dell'oggetto vtable (confrontare il nome del vtable e quello che sta indicando). Questo sarebbe necessario se si voleva fare alcun tipo di identificazione del tipo di esecuzione, dal momento che di solito coinvolge guardando l'indirizzo del vtable (o almeno qualcosa vicino alla parte anteriore di esso).

Infine, per quanto riguarda il motivo per cui ci sono le funzioni SETINT e getInt cripticamente-nome alla fine del vtable C, io sono abbastanza sicuro che è perché il tipo di eredita C due differenti set di funzioni denominate setInt e getInt - da uno a A e uno attraverso B. Se dovessi indovinare, il pressare qui è quello di garantire che i meccanismi interni del compilatore in grado di distinguere tra le due funzioni virtuali.

Spero che questo aiuti!

Altri suggerimenti

Ecco la tua corse discarica attraverso C ++ filt:

Vtable for A
A::vtable for A: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& typeinfo for A)
16    A::setInt
24    A::getInt

Class A
   size=16 align=8
   base size=12 base align=8
A (0x10209fb60) 0
    vptr=((& A::vtable for A) + 16u)

Vtable for B
B::vtable for B: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& typeinfo for B)
16    B::setInt
24    B::getInt

Class B
   size=16 align=8
   base size=12 base align=8
B (0x1020eb230) 0
    vptr=((& B::vtable for B) + 16u)

Vtable for C
C::vtable for C: 8u entries
0     (int (*)(...))0
8     (int (*)(...))(& typeinfo for C)
16    C::setInt
24    C::getInt
32    (int (*)(...))-0x00000000000000010
40    (int (*)(...))(& typeinfo for C)
48    C::non-virtual thunk to C::setInt(int)
56    C::non-virtual thunk to C::getInt()

Class C
   size=32 align=8
   base size=32 base align=8
C (0x1020f5080) 0
    vptr=((& C::vtable for C) + 16u)
  A (0x1020ebd90) 0
      primary-for C (0x1020f5080)
  B (0x1020ebe00) 16
      vptr=((& C::vtable for C) + 48u)

Non ho idea di quello che il (int (*)(...))-0x00000000000000010 e (int (*)(...))0 sono.
La parte C::_ZThn16_N1C6setIntEi/C::non-virtual thunk to C::setInt(int) è un "ottimizzazione di chiamate di funzione virtuali in presenza di ereditarietà multipla o virtuale" come descritto qui .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top