Il problema è che *ppv
è di solito un void*
- assegnando direttamente this
ad esso si limiterà a prendere la this
puntatore esistente e dare *ppv
il valore di esso (in quanto tutti i puntatori possono essere lanciati a void*
).
Questo non è un problema con l'ereditarietà singola perché con l'ereditarietà singola il puntatore di base è sempre lo stesso per tutte le classi (perché il vtable è appena esteso per le classi derivate).
Tuttavia, per l'ereditarietà multipla si finisce con più puntatori di base, a seconda di quale "vista" della classe di cui si sta parlando! La ragione di ciò è che con l'ereditarietà multipla non puoi semplicemente estendere il vtable - hai bisogno di più vtables a seconda del ramo di cui stai parlando.
Quindi è necessario eseguire il cast del puntatore this
per assicurarsi che il compilatore inserisca il puntatore di base corretto (per il vtable corretto) in *ppv
.
Ecco un esempio di ereditarietà singola:
class A {
virtual void fa0();
virtual void fa1();
int a0;
};
class B : public A {
virtual void fb0();
virtual void fb1();
int b0;
};
vtable per A:
[0] fa0
[1] fa1
vtable per la B:
[0] fa0
[1] fa1
[2] fb0
[3] fb1
Si noti che se avete la B
vtable voi e trattalo come un A
vtable funziona solo - gli scostamenti per i membri di A
sono esattamente ciò che ti aspetteresti.
Ecco un esempio utilizzando l'ereditarietà multipla (utilizzando le definizioni di A
e B
dall'alto) (nota: solo un esempio - le implementazioni possono variare):
class C {
virtual void fc0();
virtual void fc1();
int c0;
};
class D : public B, public C {
virtual void fd0();
virtual void fd1();
int d0;
};
vtable per C:
[0] fc0
[1] fc1
vtable per D:
@A:
[0] fa0
[1] fa1
[2] fb0
[3] fb1
[4] fd0
[5] fd1
@C:
[0] fc0
[1] fc1
[2] fd0
[3] fd1
E l'attuale m Layout Emory per D
:
[0] @A vtable
[1] a0
[2] b0
[3] @C vtable
[4] c0
[5] d0
Si noti che se si trattano un D
vtable come A
funzionerà (questo è un caso - non si può fare affidamento su di esso). Tuttavia, se trattate un vtable come C
quando chiamate il numero c0
(che il compilatore si aspetta nello slot 0 del vtable) chiamerete improvvisamente a0
!
Quando si chiama c0
su un D
ciò che il compilatore non è in realtà un falso passa puntatore this
che ha un vtable che sembra come dovrebbe per una C
.
Così, quando si chiama una funzione C
su D
si ha la necessità di regolare il vtable per puntare al centro dell'oggetto D
(al @C
vtable) prima di chiamare la funzione.
Come viene definito 'ppv'? – spoulson
vuoto come di consueto negli esempi di QueryInterface(). – sharptooth
Così fa: C * c = static_cast < C* > (questo); garantire un puntatore alla sottoclasse corretta? O devo usare dynamic_cast? –