Originariamente inviata da
MItaly
Un puntatore al subobject della classe base.
Prendiamo studente:
codice:
class studente:virtual public persona
{
protected:
int s;
};
il suo layout di fatto sarà equivalente a qualcosa di questo tipo:
codice:
struct studente
{
persona *_studente_baseptr;
int s;
persona _base;
}
con _studente_baseptr che viene inizializzato a &_base al momento della costruzione; lo stesso dicasi per lavoratore.
Per studente_lavoratore, la questione sarà di questo tipo:
codice:
struct studente_lavoratore
{
// subobject studente
persona *_studente_baseptr;
int s;
// subobject lavoratore
persona *_lavoratore_baseptr;
int l;
// subobject classe base condivisa
persona _base;
};
Anche qui, i due baseptr vengono inizializzati a _base al momento della costruzione.
Qual è lo scopo di tutto questo giro di puntatori? Per fare in modo che codice che si aspetta uno studente* possa funzionare correttamente con uno studente_lavoratore, facendo riferimento al subobject di classe base corretto in ogni caso.
Immaginiamo che il compilatore debba compilare del codice di questo tipo:
codice:
void foo(studente *s) {
s->p = 12;
}
void bar(lavoratore *l) {
l->p = 24;
}
int main() {
studente s;
studente_lavoratore sl;
foo(&s);
foo(&sl);
return 0;
}
Il codice diventa del tipo:
codice:
void foo(studente *s) {
s->_studente_baseptr->p = 12;
}
void bar(lavoratore *l) {
s->_lavoratore_baseptr->p = 12;
}
int main() {
studente s;
s._studente_baseptr = &s._base;
studente_lavoratore sl;
s._studente_baseptr = &sl._base;
foo(&s);
foo(&sl);
bar((lavoratore *)&_lavoratore_baseptr);
return 0;
}
Il codice di foo viene compilato una sola volta, indipendentemente dal tipo della classe derivata che può essere passato (in cui la posizione del subobject di classe base può variare), ma dato che tutti i riferimenti alla classe base avvengono in modo indiretto tramite il puntatore il risultato finale è sempre corretto. Nota tra l'altro che per passare uno studente_lavoratore ad una funzione che si aspetta un lavoratore * il compilatore deve aggiustare il puntatore prima del passaggio (deve passare un puntatore al primo membro del subobject di tipo lavoratore), ma a parte questo funzioni che si aspettano un lavoratore * funzionano in maniera trasparente.