2013-08-15 9 views
8

ho trovato questa tecnica nel seguente link: http://www.codeproject.com/Tips/90875/Displaying-vtable-when-debuggingvoid (** vt)() = * (void (***)()) ptr; una variabile di aiuto per tabella virtuale in C++

e là, egli utilizza un aiutante variabile

void (**vt)() = *(void (***)())ptr; 

per aiutare visualizzare la tabella di funzione virtuale.

Ma se cambio a

void (**vt)() = (void (**)())ptr; 

non funziona come la precedente.

Qualcuno potrebbe aiutarmi a spiegare che magia gioca qui, per favore?

risposta

3

Il puntatore del tavolo virtuale in molte implementazioni C++ è il primo sizeof(void (**)()) byte dell'oggetto. Quando si dereferenzia quel puntatore si otterrà l'indirizzo iniziale della tabella virtuale reale. Questo è il significato del codice funzionante.

Il programma cdecl potrebbe essere di po 'di aiuto qui:

cdecl> explain void (***foo)() 
declare foo as pointer to pointer to pointer to function returning void 
cdecl> explain void (**foo)() 
declare foo as pointer to pointer to function returning void 

Il primo codice getta il puntatore per l'oggetto come un puntatore correttamente derefereancable (puntatore a puntatore a puntatore a funzione, void (***)()) e quindi dereferenziare per acquisire l'indirizzo iniziale della tabella virtuale, che è di tipo void (**)() (puntatore a puntatore alla funzione), che punta all'inizio della tabella virtuale che è di tipo void (*[])() (matrice di poin ter to function).

Il secondo getta il puntatore all'oggetto su un puntatore a un puntatore a una funzione che restituisce il vuoto; l'indirizzo memorizzato nella variabile vt è solo l'indirizzo del tuo oggetto.

class Object { 
public: 
    virtual void foo() {}; 
}; 

Object x; 

// is approximately equivalent to 

struct Object { 
    void (**_vtable)(); 
}; 

void _Object_foo(Object this) { 
} 

// this does not really compile, however, 
// because a pointer mismatch 
void (*_vtable_for_Object[])() = { 
    _Object_foo 
}; 

Object y; 
y._vtable = _vtable_for_Object; 

Così facendo

Object *ptr = new Object(); 
// x now points to a block of memory, 
// whose first bytes are void (**_vtable)() 

// then 
void (**vt)() = *(void (***)())ptr; 

// is equivalent in our analogy to 
void (**vt)() = ptr->_vtable; 

// except that the C++ does not allow you to 
// access the virtual table directly without 
// the trickery of the former 
+0

Grazie per avermi spiegato questo. Molto illuminante! – Alex

8

Introduciamo un typedef per chiarezza.

typedef void (**thing)(); 

Poi il primo codice è

thing vt = *(thing*) ptr; 

e la seconda

thing vt = (thing) ptr; 

Cioè, il primo dice "treat PTR come un puntatore ad una cosa, e mi danno quello che punta a "e il secondo" tratta ptr come una cosa ".
La differenza dovrebbe essere ovvia.

Il motivo per cui funziona per tutto è molto specifico sia per il compilatore di Visual C++ che per il debugger di Visual Studio.

Se il debugger non sa che tipo è realmente l'oggetto, visualizza ciò che sa, che è un'istanza della classe base, e sa solo quante voci ha il vtable della classe base.

(Hai lasciato qui una parte cruciale, che è quella di aggiungere il numero previsto di voci di tabella all'elemento nella finestra di controllo.Questo fa sì che il debugger mostri quella memoria come una matrice di tutti gli elementi che dici.)

Quindi il trucco consiste nell'inventare un nome per il vtable e nel dire al debugger quanti elementi visualizzare dalla tabella.

+0

Penso che Alex voglia sapere perché il primo funziona? –

+0

Penso che @molbdnilo abbia spiegato bene il problema. – Alex

Problemi correlati