Ovviamente i valori del puntatore possono essere diversi! Di seguito un esempio che dimostra il problema (potrebbe essere necessario utilizzare derived1
sul sistema anziché derived2
per ottenere una differenza). Il punto è che il puntatore this
viene in genere regolato quando è coinvolta l'ereditarietà multipla virtuale. Questo potrebbe essere un caso raro, ma succede.
Un potenziale caso d'uso di questo linguaggio è quello di essere in grado di ripristinare gli oggetti di tipo noto, dopo la loro memorizzazione come void const*
(o void*
; la correttezza const
non importa qui): se si dispone di una gerarchia di ereditarietà complesso, è non può semplicemente lanciare un qualsiasi puntatore dispari a un void*
e sperare di poterlo ripristinare al suo tipo originale! Cioè, per ottenere facilmente, ad esempio, un puntatore a base
(dall'esempio seguente) e convertirlo in void*
, si chiamerebbe p->getThis()
che è molto più semplice da static_cast<base*>(p)
e si ottiene un void*
che può essere trasmesso in modo sicuro a base*
utilizzando a static_cast<base*>(v)
: è possibile invertire la conversione implicita ma solo se si esegue il cast del tipo esatto da cui proviene il puntatore originale. Vale a dire, static_cast<base*>(static_cast<void*>(d))
dove d
è un puntatore a un oggetto di un tipo derivato da base
illegale ma static_cast<base*>(d->getThis())
è legale.
Ora, perché l'indirizzo cambia in primo luogo? Nell'esempio base
è una classe base virtuale di due classi derivate ma potrebbero essercene di più. Tutti gli oggetti secondari la cui classe eredita virtualmente da base
condivideranno un oggetto comune base
in un oggetto di un'altra classe derivata (concrete
nell'esempio seguente). La posizione di questo suboggetti base
può essere diversa rispetto al rispettivo sottobooggetto derivato, a seconda di come sono ordinate le diverse classi. Di conseguenza, il puntatore all'oggetto base
è in genere diverso dai puntatori ai sottooggetti di classi che ereditano virtualmente da base
. L'offset pertinente verrà calcolato al momento della compilazione, quando possibile, o provenire da qualcosa come un vtable in fase di esecuzione. Gli offset vengono regolati durante la conversione dei puntatori lungo la gerarchia di ereditarietà.
#include <iostream>
struct base
{
void const* getThis() const { return this; }
};
struct derived1
: virtual base
{
int a;
};
struct derived2
: virtual base
{
int b;
};
struct concrete
: derived1
, derived2
{
};
int main()
{
concrete c;
derived2* d2 = &c;
void const* dptr = d2;
void const* gptr = d2->getThis();
std::cout << "dptr=" << dptr << " gptr=" << gptr << '\n';
}
fonte
2013-08-21 22:39:08
Sono presenti modelli? O ereditarietà? Il tipo restituito è dello stesso tipo della classe che contiene la funzione? Perché altrimenti non ha assolutamente senso –
In quale contesto viene chiamato 'getThis'? – Joni
sembra come se fosse per il cast di 'void *'. Perché non si sono limitati a "annullare" * Non ne ho idea. Vedi alcune cose divertenti nel codice di altre persone. – Dave